Attribute VB_Name = "GermanHealthExamples2018"
Option Explicit

' $Id: GermanHealthExamples.bas $
' Examples using CryptoSys PKI to create and read signed-and-enveloped PKCS7 (CMS) objects suitable for
' the Security interface for data exchange in the health service version [2018].
'   Last Updated:
'   $Date: 2018-04-29 17:31:00 $
'   $Revision: 1.3.0 $

' ******************************************************************************
' Copyright (c) 2007-18 DI Management Services Pty Limited. All rights reserved.
' Provided to illustrate the use of functions in the CryptoSys PKI Toolkit.
' Not necessarily a good example of secure programming techniques.
' Provided "as is" with no warranties. Use at your own risk.
' ******************************************************************************

' Path to working directory - CHANGE THIS TO SUIT YOUR SYSTEM
' (with trailing \) or leave empty to use current working dir
Private Const TESTPATH As String = "C:\Test\GermanHealth\"


'********************
' GENERIC FUNCTIONS *
' *******************
Public Function ghs2018_Make_Signed_And_EnvelopedData( _
    strOutputFile As String, _
    strMsg As String, _
    strPriKeyFile As String, _
    strPassword As String, _
    strSignersCertList As String, _
    strRecipCertFile As String, _
    Optional fKeepInterFile As Boolean _
) As Long

    Dim nRet As Long
    Dim strPrivateKey As String
    Dim strSigFile As String
    
    ' Intermediate signed-data file we will create
    strSigFile = strOutputFile & ".int.tmp"
    
    ' Read in the private key string
    Debug.Print "Reading private key from PRI file..."
    strPrivateKey = rsaReadPrivateKey(strPriKeyFile, strPassword)
    ' Check for success
    If Len(strPrivateKey) = 0 Then
        MsgBox "Cannot read private key", vbCritical
        Exit Function
    Else
        Debug.Print "...OK, read private key: key length=" & RSA_KeyBits(strPrivateKey) & " bits"
    End If
    
    ' Create a signed-data object with signed attributes and signing-time and all certificates.
    nRet = CMS_MakeSigDataFromString(strSigFile, strMsg, strSignersCertList, _
        strPrivateKey, PKI_CMS_INCLUDE_ATTRS + PKI_CMS_ADD_SIGNTIME _
        + PKI_SIG_RSA_PSS_SHA256)  ' NEW IN 2018
    Debug.Print "CMS_MakeSigDataFromString returns " & nRet & " (expecting 0)"
    ' Clean up as we go
    Call WIPE_String(strPrivateKey, Len(strPrivateKey))
    ' Check for success
    If nRet <> 0 Then
        Debug.Print "ERROR (" & nRet & "): " & pkiErrorLookup(nRet) & ": " & pkiGetLastError()
        MsgBox "Cannot create signed-data file", vbCritical
        Exit Function
    Else
        Debug.Print "OK, created signed-data file '" & strSigFile & "'"
    End If
    
    ' Now we encrypt the signed-data object directly using the recipient's certificate
    ' this produces a binary enveloped-data file
    nRet = CMS_MakeEnvData(strOutputFile, strSigFile, strRecipCertFile, "", 0, _
        PKI_KT_RSAES_OAEP + PKI_HASH_SHA256 + PKI_BC_AES256)  ' NEW IN 2018
    Debug.Print "CMS_MakeEnvData returns " & nRet & " (expecting 1)"
    If nRet <= 0 Then
        Debug.Print "ERROR (" & nRet & "): " & pkiErrorLookup(nRet) & ": " & pkiGetLastError()
        MsgBox "Cannot create enveloped-data file", vbCritical
        Exit Function
    Else
        Debug.Print "OK, created enveloped-data file '" & strOutputFile & "'"
    End If
    
    ' Clean up sensitive data
    If Not fKeepInterFile Then
        Call WIPE_File(strSigFile, 0)
    End If
    
    ' Now send the output file to the recipient...
    
End Function

Public Function ghs2018_Read_Signed_and_Enveloped_Data( _
    strInputFile As String, _
    strPriKeyFile As String, _
    strPassword As String, _
    Optional strCertFile As String, _
    Optional fKeepInterFile As Boolean _
) As String
' Returns string containing output message or an empty string on error
    Dim nRet As Long
    Dim strPrivateKey As String
    Dim strSigFile As String
    Dim strOutput As String
    Dim strQuery As String
       
    ' Read in the recipient's private key
    strPrivateKey = rsaReadPrivateKey(strPriKeyFile, strPassword)
    If Len(strPrivateKey) = 0 Then
        Debug.Print "ERROR:  " & pkiGetLastError()
        MsgBox "Cannot read private key", vbCritical
        Exit Function
    End If
    
    ' Intermediate file we will create
    strSigFile = strInputFile & ".i2.tmp"
    
    ' Read the encrypted data from the enveloped-data file
    nRet = CMS_ReadEnvData(strSigFile, strInputFile, "", strPrivateKey, 0)
    Debug.Print "CMS_ReadEnvData returns " & nRet & " (expected 0)"
    If nRet <> 0 Then
        Debug.Print "ERROR (" & nRet & "): " & pkiErrorLookup(nRet) & ": " & pkiGetLastError()
    Else
        Debug.Print "Extracted signed-data file '" & strSigFile & "'"
    End If
    
    ' Pre-dimension output string for query result
    strOutput = String(64, " ")
    
    Debug.Print "For SigData file '" & strSigFile & "'..."
    nRet = CMS_QuerySigData(strOutput, Len(strOutput), strSigFile, "version", 0)
    Debug.Print "Version=" & nRet
    nRet = CMS_QuerySigData(strOutput, Len(strOutput), strSigFile, "signingTime", 0)
    If nRet > 0 Then
        Debug.Print "signingTime=" & Left$(strOutput, nRet)
    Else
        Debug.Print "ERROR=" & nRet
    End If
    strQuery = "messageDigest"
    nRet = CMS_QuerySigData(strOutput, Len(strOutput), strSigFile, strQuery, 0)
    If nRet > 0 Then
        Debug.Print strQuery & "=" & Left$(strOutput, nRet)
    Else
        Debug.Print "ERROR=" & nRet
    End If
    
    strQuery = "CountOfSignerInfos"
    nRet = CMS_QuerySigData(strOutput, Len(strOutput), strSigFile, strQuery, 0)
    If nRet > 0 Then
        Debug.Print strQuery & "=" & nRet
    Else
        Debug.Print "ERROR=" & nRet
    End If
    
    strQuery = "CountOfCertificates"
    nRet = CMS_QuerySigData(strOutput, Len(strOutput), strSigFile, strQuery, 0)
    If nRet > 0 Then
        Debug.Print strQuery & "=" & nRet
    Else
        Debug.Print "ERROR=" & nRet
    End If
    
    nRet = CMS_VerifySigData(strSigFile, "", "", 0)
    Debug.Print "CMS_VerifySigData('') returns " & nRet & " (expecting 0)"

    nRet = CMS_VerifySigData(strSigFile, strCertFile, "", 0)
    Debug.Print "CMS_VerifySigData('" & strCertFile & "') returns " & nRet & " (expecting 0)"

    Dim nDataLen As Long
    Dim strData As String
    ' How long is the content to be read?
    nDataLen = CMS_ReadSigDataToString("", 0, strSigFile, 0)
    If nDataLen <= 0 Then
        Debug.Print "ERROR (" & nRet & "): " & pkiErrorLookup(nRet) & ": " & pkiGetLastError()
        MsgBox "Cannot read signed-data file", vbCritical
        Exit Function
    End If
    ' Pre-dimension string to receive data
    strData = String(nDataLen, " ")
    nRet = CMS_ReadSigDataToString(strData, nDataLen, strSigFile, 0)
    Debug.Print "CMS_ReadSigDataToString returns " & nRet
    Debug.Print "Data is [" & strData & "]"
    
    ' Return message string
    ghs2018_Read_Signed_and_Enveloped_Data = strData
    
    ' Clean up sensitive data
    If Not fKeepInterFile Then
        Call WIPE_File(strSigFile, 0)
    End If
    Call WIPE_String(strData, Len(strData))

End Function

' ***********************************
' TEST PROGRAMS FOR ABOVE FUNCTIONS *
' ***********************************
Public Sub ghs2018_Test_Make_Signed_And_EnvelopedData()
    Dim nRet As Long
    Dim strOutputFile As String
    Dim strMsg As String
    Dim strPriKeyFile As String
    Dim strPassword As String
    Dim strSignersCertList As String
    Dim strRecipCertFile As String
    
    ' The message we want to send
    strMsg = "Hallo Walt"
    ' Final enveloped-data file to send to recipient
    strOutputFile = TESTPATH & "To_999009051b.p7m"
    ' Our private key data with 4096-bit key
    strPriKeyFile = TESTPATH & "999009991b_pri.p8e"
    strPassword = "password"    ' CAUTION: DO NOT HARDCODE YOUR PRODUCTION PASSWORD!
    ' Signer's certificate plus (optionally) the certs in the chain that signed it. Separated by a semi-colon ";".
    ' NOTE: the first cert in the list MUST be the signers
    strSignersCertList = TESTPATH & "999009991b.cer" & ";" & TESTPATH & "Int_4096_Cert.cer" & ";" & TESTPATH & "CA_4096_Cert.cer"
    ' The certificate of the recipient -- this must be provided (otherwise we wouldn't know whom to send it to)
    strRecipCertFile = TESTPATH & "999009051b.cer"

    nRet = ghs2018_Make_Signed_And_EnvelopedData(strOutputFile, strMsg, strPriKeyFile, strPassword, strSignersCertList, strRecipCertFile, fKeepInterFile:=True)
    Debug.Print "ghs_Make_Signed_And_EnvelopedData returns " & nRet & " (expecting zero)"
    
    ' Clean up password string
    Call WIPE_String(strPassword, Len(strPassword))
End Sub

Public Sub ghs2018_Test_Read_Signed_and_Enveloped_Data()
' NOTE: We can only do this because we have the private key for the dummy user with id IK999009051.
' To test yourself, send a test message to yourself signed by yourself.
    Dim strMsg As String
    Dim strInputFile As String
    Dim strPriKeyFile As String
    Dim strPassword As String
    Dim strCertFile As String
    
    ' Input data file
    strInputFile = TESTPATH & "To_999009051b.p7m"
    ' Recipient's private key data (4096 bits)
    strPriKeyFile = TESTPATH & "999009051b_pri.p8e"
    strPassword = "password"    ' CAUTION: DO NOT HARDCODE YOUR PRODUCTION PASSWORD!
    ' The certificate of the sender -- optional
    strCertFile = ""
    
    strMsg = ghs2018_Read_Signed_and_Enveloped_Data(strInputFile, strPriKeyFile, strPassword)
    Debug.Print "ghs2018_Read_Signed_and_Enveloped_Data returns '" & strMsg & "'"
    
    ' Clean up password string
    Call WIPE_String(strPassword, Len(strPassword))
End Sub