Attribute VB_Name = "basTestChile"
' $Id: basTestChile $
' $Date: 2009-05-07 17:41 $
' $Author: dai $

' This module uses functions from the CryptoSys (tm) PKI Toolkit available from
' <www.cryptosys.net/pki/>.
' Include the module `basCrPKI' in your project.

' *************************** COPYRIGHT NOTICE ******************************
' This code was originally written by David Ireland and is copyright
' (C) 2008-9 DI Management Services Pty Ltd <www.di-mgt.com.au>.
' Provided "as is". No warranties. Use at your own risk. You must make your
' own assessment of its accuracy and suitability for your own purposes.
' It is not to be altered or distributed, except as part of an application.
' You are free to use it in any application, provided this copyright notice
' is left unchanged.
' ************************ END OF COPYRIGHT NOTICE **************************

' NOTES: Some of these procedures are specific for the Chile SII examples.
' Other procedures are more generic and could be used in more general PKI
' programs, perhaps with minor amendments.
' These are marked $GENERIC-FUNCTION$.

Option Explicit

' Ref: F60T33-ejemplo.xml

 Public Sub Read_SmallerKeys()
    ' From manual_certificacion.pdf, 2003-11-26, p25
    Dim strKeyPem As String
    Dim nLen As Long
    Dim nRet As Long
    Dim strPrivateKey As String
    Dim strXmlKey As String
    Dim strPublicKey As String
    Dim strSig64 As String
    Dim abData() As Byte
    Dim nDataLen As Long
    Dim nKeyLen As Long
    Dim abDigest() As Byte
    Dim nDigLen As Long
    
    ' CAUTION: Your real Private Key should be kept SECRET and should never be hard-coded in code like, er, this...
    ' Put PEM data into a string
    ' (Note two critical vbCrLF's added before and after base64 data)
    strKeyPem = "-----BEGIN RSA PRIVATE KEY-----" & vbCrLf & _
"MIIBOwIBAAJBANGuDuim8fEI9yuIlkj+MOyp3mWHifoP6a4oWLSBKJSrd3MpEsZd" & _
"czvL0l7t/e0IU5rF+0gRLnU1Mfvtsw1wYWcCAQMCQQCLyV9FxKFLW09yWw7bVCCd" & _
"xpRDr7FRX/EexZB4VhsNxm/vtJfDZyYle0Lfy42LlcsXxPm1w6Q6NnjuW+AeBy67" & _
"AiEA7iMi5q5xjswqq+49RP55o//jqdZL/pC9rdnUKxsNRMMCIQDhaHdIctErN2hC" & _
"IP9knS3+9zra4R+5jSXOvI+3xVhWjQIhAJ7CF0R0S7SIHHKe04NUURf/7RvkMqm1" & _
"08k74sdnXi3XAiEAlkWk2vc2HM+a1sCqQxNz/098ketqe7NuidMKeoOQObMCIQCk" & _
"FAMS9IcPcMjk7zI2r/4EEW63PSXyN7MFAX7TYe25mw==" & vbCrLf & _
"-----END RSA PRIVATE KEY-----"

    ' Read the PEM private key data into an internal key string we can use later on...
    ' -- this example key is stored in a PKCS#8 PrivateKeyInfo without a password :-(
    nLen = RSA_ReadPrivateKeyInfo("", 0, strKeyPem, 0)
    If nLen <= 0 Then
        Debug.Print "ERROR: cannot read PEM key. Error " & nRet
        Exit Sub
    End If
    strPrivateKey = String(nLen, " ")
    nLen = RSA_ReadPrivateKeyInfo(strPrivateKey, Len(strPrivateKey), strKeyPem, 0)
    Debug.Print "Private key is " & RSA_KeyBits(strPrivateKey) & " bits"
    
    ' Now we compare this private key with the published public key in the XML file...
    
    ' Form XML string of public key in proper XMLSIG format using <RSAPK> element from XML file
    ' <RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK>
    ' NOTE: We have changed the element names to the required XMLSIG format:
    ' <RSAPK> --> <RSAKeyValue>
    ' <M>     --> <Modulus>
    ' <E>     --> <Exponent>
    strXmlKey = "<RSAKeyValue>" & _
        "<Modulus>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</Modulus>" & _
        "<Exponent>Aw==</Exponent></RSAKeyValue>"
    ' Convert XML form to our internal format
    nLen = RSA_FromXMLString("", 0, strXmlKey, 0)
    If nLen <= 0 Then
        Debug.Print "ERROR: Unable to read XML key string"
        Exit Sub
    End If
    strPublicKey = String(nLen, " ")
    nLen = RSA_FromXMLString(strPublicKey, Len(strPublicKey), strXmlKey, 0)
    Debug.Print "Public key is " & RSA_KeyBits(strPublicKey) & " bits"
   
    ' Check that key pairs match...
    nRet = RSA_KeyMatch(strPrivateKey, strPublicKey)
    Debug.Print "RSA_KeyMatch returns " & nRet & " (expecting 0)"
    
    ' Then use the PUBLIC key to extract the message digest from the signature value...
    
    ' Extract digest from FRMT
    ' <FRMT algoritmo="SHA1withRSA">GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8i
    ' S4HXvJj3oYZuey53Krniew==</FRMT>
    strSig64 = "GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8iS4HXvJj3oYZuey53Krniew=="
    
    ' Convert signatureValue to byte format
    abData = cnvBytesFromB64Str(strSig64)
    Debug.Print "SIGNATURE=" & cnvHexStrFromBytes(abData)
    nDataLen = UBound(abData) - LBound(abData) + 1
    Debug.Print "Signature is " & nDataLen & " bytes long"
    ' Check the length
    nKeyLen = RSA_KeyBytes(strPublicKey)
    Debug.Print "Key is " & nKeyLen & " bytes long"
    If nKeyLen <> nDataLen Then
        Debug.Print "ERROR: Key is the wrong length!"
        Exit Sub
    End If
    ' Decrypt the signature
    nRet = RSA_RawPublic(abData(0), nDataLen, strPublicKey, 0)
    Debug.Print "RSA_RawPublic returns " & nRet & " (expecting 0)"
    If nRet <> 0 Then
        Debug.Print "ERROR: " & pkiErrorLookup(nRet)
        Exit Sub
    End If
    Debug.Print "EMSA BLOCK=" & cnvHexStrFromBytes(abData)
    
    ' Extract digest from block
    nDigLen = RSA_DecodeMsg(0, 0, abData(0), nDataLen, PKI_EMSIG_PKCSV1_5)
    Debug.Print "RSA_DecodeMsg returns " & nDigLen & " (expecting 20)"
    If nDigLen <= 0 Then
        Debug.Print "ERROR: Cannot extract digest"
        Exit Sub
    End If
    ReDim abDigest(nDigLen - 1)
    nRet = RSA_DecodeMsg(abDigest(0), nDigLen, abData(0), nDataLen, PKI_EMSIG_PKCSV1_5)
    
    ' Display digest bytes as hex and base64
    Debug.Print "DIGEST=" & cnvHexStrFromBytes(abDigest)
    Debug.Print "base64=" & cnvB64StrFromBytes(abDigest)

End Sub

 Public Sub MakeFRMTdigest()
' This should give the same digest value we obtained above.
    Dim strData As String
    Dim strDigest As String
    Dim nLen As Long
    ' Put <DD> data into a single string with no spaces between elements (and fix double-quotes for VB when hard-coded here!)
    strData = "<DD><RE>97975000-5</RE><TD>33</TD><F>60</F><FE>2003-10-13</FE><RR>77777777-7</RR><RSR>EMPRESA  LTDA</RSR><MNT>119000</MNT><IT1>Parlantes Multimedia 180W.</IT1><CAF version=""1.0""><DA><RE>97975000-5</RE><RS>RUT DE PRUEBA</RS><TD>33</TD><RNG><D>1</D><H>200</H></RNG><FA>2003-09-04</FA><RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK><IDK>100</IDK></DA><FRMA algoritmo=""SHA1withRSA"">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA></CAF><TSTED>2003-10-13T09:33:20</TSTED></DD>"
    ' Create an SHA-1 digest value in hex format
    strDigest = String(PKI_SHA1_CHARS, " ")
    nLen = HASH_HexFromString(strDigest, Len(strDigest), strData, Len(strData), PKI_HASH_SHA1)
    Debug.Print "SHA1(DD)=" & strDigest
    ' Display in base64
    Debug.Print "SHA1(DD)=" & cnvB64StrFromBytes(cnvBytesFromHexStr(strDigest))
End Sub

 Public Sub SignDDtoMakeFRMTsig()
' Use the RSA private key to sign the <DD> data.
' INPUT:  RSA private key in PEM format
'         Data-to-be signed
' OUTPUT: SignatureValue in base64 form

    Dim strKeyPem As String
    Dim strData As String
    Dim nLen As Long
    Dim nRet As Long
    Dim strPrivateKey As String
    Dim nKeyBytes As Long
    Dim abBlock() As Byte
    Dim abData() As Byte
    Dim nDataLen As Long
    Dim strSigVal64 As String
    
    ' Input data:
    strData = "<DD><RE>97975000-5</RE><TD>33</TD><F>60</F><FE>2003-10-13</FE><RR>77777777-7</RR><RSR>EMPRESA  LTDA</RSR><MNT>119000</MNT><IT1>Parlantes Multimedia 180W.</IT1><CAF version=""1.0""><DA><RE>97975000-5</RE><RS>RUT DE PRUEBA</RS><TD>33</TD><RNG><D>1</D><H>200</H></RNG><FA>2003-09-04</FA><RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK><IDK>100</IDK></DA><FRMA algoritmo=""SHA1withRSA"">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA></CAF><TSTED>2003-10-13T09:33:20</TSTED></DD>"
    strKeyPem = "-----BEGIN RSA PRIVATE KEY-----" & vbCrLf & _
"MIIBOwIBAAJBANGuDuim8fEI9yuIlkj+MOyp3mWHifoP6a4oWLSBKJSrd3MpEsZd" & _
"czvL0l7t/e0IU5rF+0gRLnU1Mfvtsw1wYWcCAQMCQQCLyV9FxKFLW09yWw7bVCCd" & _
"xpRDr7FRX/EexZB4VhsNxm/vtJfDZyYle0Lfy42LlcsXxPm1w6Q6NnjuW+AeBy67" & _
"AiEA7iMi5q5xjswqq+49RP55o//jqdZL/pC9rdnUKxsNRMMCIQDhaHdIctErN2hC" & _
"IP9knS3+9zra4R+5jSXOvI+3xVhWjQIhAJ7CF0R0S7SIHHKe04NUURf/7RvkMqm1" & _
"08k74sdnXi3XAiEAlkWk2vc2HM+a1sCqQxNz/098ketqe7NuidMKeoOQObMCIQCk" & _
"FAMS9IcPcMjk7zI2r/4EEW63PSXyN7MFAX7TYe25mw==" & vbCrLf & _
"-----END RSA PRIVATE KEY-----"

    ' Read the PEM private key data into an internal key string
    nLen = RSA_ReadPrivateKeyInfo("", 0, strKeyPem, 0)
    If nLen <= 0 Then
        Debug.Print "ERROR: cannot read PEM key"
        Exit Sub
    End If
    strPrivateKey = String(nLen, " ")
    nLen = RSA_ReadPrivateKeyInfo(strPrivateKey, Len(strPrivateKey), strKeyPem, 0)
    Debug.Print "Private key is " & RSA_KeyBits(strPrivateKey) & " bits long"
    
    ' Convert the input data string into a byte array
    abData = StrConv(strData, vbFromUnicode)
    Debug.Print "Data=(0x)" & cnvHexStrFromBytes(abData)
    nDataLen = UBound(abData) + 1
    Debug.Print "Input data is " & nDataLen & " bytes long"
    
    ' Encode into a block the exact same length as the RSA key...
    
    ' Get the length of the key in bytes -- we need an output block exactly this length
    nKeyBytes = RSA_KeyBytes(strPrivateKey)
    Debug.Print "Private key is  " & nKeyBytes & " bytes long"
    ' NB: VB6 arrays of N bytes are defined from 0..N-1
    ReDim abBlock(nKeyBytes - 1)
    
    ' Encode the data -- this computes the message digest and then encodes into an EMSA block in one step
    nRet = RSA_EncodeMsg(abBlock(0), nKeyBytes, abData(0), nDataLen, PKI_EMSIG_PKCSV1_5 + PKI_HASH_SHA1)
    If nRet <> 0 Then
        Debug.Print "ERROR: RSA_EncodeMsg failed. Error code " & nRet
        Exit Sub
    End If
    Debug.Print "EMSA BLOCK=" & cnvHexStrFromBytes(abBlock)
    
    ' Sign, i.e. encrypt, the encoded block with the RSA private key
    nRet = RSA_RawPrivate(abBlock(0), nKeyBytes, strPrivateKey, 0)
    If nRet <> 0 Then
        Debug.Print "ERROR: RSA_RawPrivate failed. Error code " & nRet
        Exit Sub
    End If
    Debug.Print "SIGNATURE= " & cnvHexStrFromBytes(abBlock)
    
    ' Convert byte array to base64
    strSigVal64 = cnvB64StrFromBytes(abBlock)
    Debug.Print "SignatureValue=" & strSigVal64
    
End Sub

 Public Function XmlHexFromInternalKey(strKey As String) As String
' $GENERIC-FUNCTION$
' INPUT:  Public or private RSA key in internal format
' OUTPUT: String containing RSAKeyValue in XML format
    Dim strXmlKey As String
    Dim nLen As Long
    Dim nOptions As Long
    
    nOptions = PKI_XML_HEXBINARY + PKI_XML_EXCLPRIVATE + PKI_XML_RSAKEYVALUE
    nLen = RSA_ToXMLString("", 0, strKey, nOptions)
    If nLen > 0 Then
        strXmlKey = String(nLen, " ")
        nLen = RSA_ToXMLString(strXmlKey, Len(strXmlKey), strKey, nOptions)
        XmlHexFromInternalKey = strXmlKey
    Else
        XmlHexFromInternalKey = "**Failed to create public key in XML"
    End If
    
End Function

 Public Function IsCertAndKeyValueMatched(strCert64 As String, strXmlKey As String) As Boolean
' $GENERIC-FUNCTION$
' Compare the public key values in <RSAKeyValue> and <X509Certificate> values
' INPUT:  RSAKeyValue in base64 string INCLUDING <RSAKeyValue> tags;
'         X509Certificate in base64 string EXCLUDING <X509Certificate> tags
' OUTPUT: True if public key matches; otherwise False.
' REMARKS: The X509Certificate contains the public key value plus other info.

    Dim strPublicKey As String
    Dim strPublicKey1 As String
    Dim nRet As Long
    Dim strQuery As String
    Dim strOutput As String
    Dim nLen As Long
    Dim nHashCode1 As Long
    Dim nHashCode2 As Long
    
    strOutput = String(512, " ")
    
    ' Query the certificate for details...
    strQuery = "issuerName"
    nRet = X509_QueryCert(strOutput, Len(strOutput), strCert64, strQuery, 0)
    If nRet <= 0 Then
        Debug.Print "ERROR: Error " & nRet & " reading certificate"
        Exit Function ' ERROR
    End If
    Debug.Print strQuery & "=" & Left(strOutput, nRet)
    
    strQuery = "serialNumber"
    nRet = X509_QueryCert(strOutput, Len(strOutput), strCert64, strQuery, 0)
    If nRet <= 0 Then
        Debug.Print "ERROR: Error " & nRet & " reading certificate"
        Exit Function ' ERROR
    End If
    Debug.Print strQuery & "=" & Left(strOutput, nRet)
    
    strQuery = "subjectName"
    nRet = X509_QueryCert(strOutput, Len(strOutput), strCert64, strQuery, 0)
    If nRet <= 0 Then
        Debug.Print "ERROR: Error " & nRet & " reading certificate"
        Exit Function ' ERROR
    End If
    Debug.Print strQuery & "=" & Left(strOutput, nRet)
    
    strQuery = "notBefore"
    nRet = X509_QueryCert(strOutput, Len(strOutput), strCert64, strQuery, 0)
    If nRet <= 0 Then
        Debug.Print "ERROR: Error " & nRet & " reading certificate"
        Exit Function ' ERROR
    End If
    Debug.Print strQuery & "=" & Left(strOutput, nRet)
    
    strQuery = "signatureAlgorithm"
    nRet = X509_QueryCert(strOutput, Len(strOutput), strCert64, strQuery, 0)
    If nRet <= 0 Then
        Debug.Print "ERROR: Error " & nRet & " reading certificate"
        Exit Function ' ERROR
    End If
    Debug.Print strQuery & "=" & Left(strOutput, nRet)

    ' Extract public key from X.509 certificate
    nLen = RSA_GetPublicKeyFromCert("", 0, strCert64, 0)
    If nLen <= 0 Then
        Debug.Print "ERROR: Error reading certificate key"
        Exit Function ' ERROR
    End If
    strPublicKey = String(nLen, " ")
    nLen = RSA_GetPublicKeyFromCert(strPublicKey, Len(strPublicKey), strCert64, 0)
    Debug.Print "Public key extracted from <X509Certificate>:"
    Debug.Print "Public key is " & RSA_KeyBits(strPublicKey) & " bits"
    nHashCode1 = RSA_KeyHashCode(strPublicKey)
    Debug.Print "Public key hashcode = " & Hex(nHashCode1)
    
    ' While we're here, display the public key in XML format
    Debug.Print XmlHexFromInternalKey(strPublicKey)
    
     ' Convert XML form to our internal format (this requires the <RSAKeyvalue> tags)
    nLen = RSA_FromXMLString("", 0, strXmlKey, 0)
    If nLen <= 0 Then
        Debug.Print "ERROR: Unable to read XML key string"
        Exit Function
    End If
    strPublicKey = String(nLen, " ")
    nLen = RSA_FromXMLString(strPublicKey, Len(strPublicKey), strXmlKey, 0)
    Debug.Print "Public key given in <RSAKeyValue>:"
    Debug.Print "Public key is " & RSA_KeyBits(strPublicKey) & " bits"
    nHashCode2 = RSA_KeyHashCode(strPublicKey)
    Debug.Print "Public key hashcode = " & Hex(nHashCode2)
    Debug.Print XmlHexFromInternalKey(strPublicKey)
    
    If nHashCode1 = nHashCode2 Then
        IsCertAndKeyValueMatched = True
        Debug.Print "OK, public keys match."
    Else
        IsCertAndKeyValueMatched = False
        Debug.Print "ERROR: public keys do not match."
    End If

End Function

 Public Sub CheckExampleCertAndRSAKey()
' Ref: F60T33-ejemplo.xml, 2003-10-13

' <RSAKeyValue>
' <Modulus>
' tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx
' iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx
' BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=
' </Modulus>
' <Exponent>
' AQAB
' </Exponent>
' </RSAKeyValue>

' <X509Certificate>MIIEgjCCA+ugAwIBAgIEAQAApzANBgkqhkiG9w0BAQUFADCBtTELMAkGA1UEBhMC
' Q0wxHTAbBgNVBAgUFFJlZ2lvbiBNZXRyb3BvbGl0YW5hMREwDwYDVQQHFAhTYW50
' aWFnbzEUMBIGA1UEChQLRS1DRVJUQ0hJTEUxIDAeBgNVBAsUF0F1dG9yaWRhZCBD
' ZXJ0aWZpY2Fkb3JhMRcwFQYDVQQDFA5FLUNFUlRDSElMRSBDQTEjMCEGCSqGSIb3
' DQEJARYUZW1haWxAZS1jZXJ0Y2hpbGUuY2wwHhcNMDMxMDAxMTg1ODE1WhcNMDQw
' OTMwMDAwMDAwWjCBuDELMAkGA1UEBhMCQ0wxFjAUBgNVBAgUDU1ldHJvcG9saXRh
' bmExETAPBgNVBAcUCFNhbnRpYWdvMScwJQYDVQQKFB5TZXJ2aWNpbyBkZSBJbXB1
' ZXN0b3MgSW50ZXJub3MxDzANBgNVBAsUBlBpc28gNDEjMCEGA1UEAxQaV2lsaWJh
' bGRvIEdvbnphbGV6IENhYnJlcmExHzAdBgkqhkiG9w0BCQEWEHdnb256YWxlekBz
' aWkuY2wwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALxZlVh1xr9sKQIBDF/6
' Va+lsHQSG5AAmCWvtNTIOXN3E9EQCy7pOPHrDg6EusvoHyesZSKJbc0TnIFXZp78
' q7mbdHijzKqvMmyvwbdP7KK8LQfwf84W4v9O8MJeUHlbJGlo5nFACrPAeTtONbHa
' ReyzeMDv2EganNEDJc9c+UNfAgMBAAGjggGYMIIBlDAjBgNVHREEHDAaoBgGCCsG
' AQQBwQEBoAwWCjA3ODgwNDQyLTQwCQYDVR0TBAIwADA8BgNVHR8ENTAzMDGgL6At
' hitodHRwOi8vY3JsLmUtY2VydGNoaWxlLmNsL2UtY2VydGNoaWxlY2EuY3JsMCMG
' A1UdEgQcMBqgGAYIKwYBBAHBAQKgDBYKOTY5MjgxODAtNTAfBgNVHSMEGDAWgBTg
' KP3S4GBPs0brGsz1CJEHcjodCDCB0AYDVR0gBIHIMIHFMIHCBggrBgEEAcNSBTCB
' tTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5lLWNlcnRjaGlsZS5jbC8yMDAwL0NQ
' Uy8wgYEGCCsGAQUFBwICMHUac0VsIHRpdHVsYXIgaGEgc2lkbyB2YWxpZG8gZW4g
' Zm9ybWEgcHJlc2VuY2lhbCwgcXVlZGFuZG8gZWwgQ2VydGlmaWNhZG8gcGFyYSB1
' c28gdHJpYnV0YXJpbywgcGFnb3MsIGNvbWVyY2lvIHkgb3Ryb3MwCwYDVR0PBAQD
' AgTwMA0GCSqGSIb3DQEBBQUAA4GBABMfCyJF0mNXcov8iEWvjGFyyPTsXwvsYbbk
' OJ41wjaGOFMCInb4WY0ngM8BsDV22bGMs8oLyX7rVy16bGA8Z7WDUtYhoOM7mqXw
' /Hrpqjh3JgAf8zqdzBdH/q6mAbdvq/yb04JHKWPC7fMFuBoeyVWAnhmuMZfReWQi
' MUEHGGIW</X509Certificate>

    Dim strCert64 As String
    Dim strXmlKey As String
    
    strCert64 = "MIIEgjCCA+ugAwIBAgIEAQAApzANBgkqhkiG9w0BAQUFADCBtTELMAkGA1UEBhMCQ0wxHTAbBgNVBAgUFFJlZ2lvbiBNZXRyb3BvbGl0YW5hMREwDwYDVQQHFAhTYW50aWFnbzEUMBIGA1UEChQLRS1DRVJUQ0hJTEUxIDAeBgNVBAsUF0F1dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhMRcwFQYDVQQDFA5FLUNFUlRDSElMRSBD" & _
"QTEjMCEGCSqGSIb3DQEJARYUZW1haWxAZS1jZXJ0Y2hpbGUuY2wwHhcNMDMxMDAxMTg1ODE1WhcNMDQwOTMwMDAwMDAwWjCBuDELMAkGA1UEBhMCQ0wxFjAUBgNVBAgUDU1ldHJvcG9saXRhbmExETAPBgNVBAcUCFNhbnRpYWdvMScwJQYDVQQKFB5TZXJ2aWNpbyBkZSBJbXB1ZXN0b3MgSW50ZXJub3MxDzANBgNVBAsU" & _
"BlBpc28gNDEjMCEGA1UEAxQaV2lsaWJhbGRvIEdvbnphbGV6IENhYnJlcmExHzAdBgkqhkiG9w0BCQEWEHdnb256YWxlekBzaWkuY2wwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALxZlVh1xr9sKQIBDF/6Va+lsHQSG5AAmCWvtNTIOXN3E9EQCy7pOPHrDg6EusvoHyesZSKJbc0TnIFXZp78q7mbdHijzKqvMmyv" & _
"wbdP7KK8LQfwf84W4v9O8MJeUHlbJGlo5nFACrPAeTtONbHaReyzeMDv2EganNEDJc9c+UNfAgMBAAGjggGYMIIBlDAjBgNVHREEHDAaoBgGCCsGAQQBwQEBoAwWCjA3ODgwNDQyLTQwCQYDVR0TBAIwADA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmUtY2VydGNoaWxlLmNsL2UtY2VydGNoaWxlY2EuY3JsMCMG" & _
"A1UdEgQcMBqgGAYIKwYBBAHBAQKgDBYKOTY5MjgxODAtNTAfBgNVHSMEGDAWgBTgKP3S4GBPs0brGsz1CJEHcjodCDCB0AYDVR0gBIHIMIHFMIHCBggrBgEEAcNSBTCBtTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5lLWNlcnRjaGlsZS5jbC8yMDAwL0NQUy8wgYEGCCsGAQUFBwICMHUac0VsIHRpdHVsYXIgaGEgc2lk" & _
"byB2YWxpZG8gZW4gZm9ybWEgcHJlc2VuY2lhbCwgcXVlZGFuZG8gZWwgQ2VydGlmaWNhZG8gcGFyYSB1c28gdHJpYnV0YXJpbywgcGFnb3MsIGNvbWVyY2lvIHkgb3Ryb3MwCwYDVR0PBAQDAgTwMA0GCSqGSIb3DQEBBQUAA4GBABMfCyJF0mNXcov8iEWvjGFyyPTsXwvsYbbkOJ41wjaGOFMCInb4WY0ngM8BsDV22bGM" & _
"s8oLyX7rVy16bGA8Z7WDUtYhoOM7mqXw/Hrpqjh3JgAf8zqdzBdH/q6mAbdvq/yb04JHKWPC7fMFuBoeyVWAnhmuMZfReWQiMUEHGGIW"

    strXmlKey = "<RSAKeyValue><Modulus>" & _
        "tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx" & _
        "iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx" & _
        "BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=</Modulus>" & _
        "<Exponent>AQAB</Exponent></RSAKeyValue>"

    Debug.Print IsCertAndKeyValueMatched(strCert64, strXmlKey)
        
End Sub

 Public Sub CheckManualCertAndRSAKey()
' Ref: manual_certificacion.pdf, 2003-11-26

' <RSAKeyValue>
' <Modulus>
' tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx
' iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx
' BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=
' </Modulus>
' <Exponent>
' AQAB
' </Exponent>
' </RSAKeyValue>

' <X509Certificate>MIIEPjCCA6mgAwIBAgIDAgGKMAsGCSqGSIb3DQEBBDCBsTEdMBsGA1UECBQUUmVn
' aW9uIE1ldHJvcG9saXRhbmExETAPBgNVBAcUCFNhbnRpYWdvMSIwIAYDVQQDFBlF
' LUNlcnRjaGlsZSBDQSBJbnRlcm1lZGlhMTYwNAYDVQQLFC1FbXByZXNhIE5hY2lv
' bmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExFDASBgNVBAoUC0UtQ0VS
' VENISUxFMQswCQYDVQQGEwJDTDAeFw0wMjEwMDIxOTExNTlaFw0wMzEwMDIwMDAw
' MDBaMIHXMR0wGwYDVQQIFBRSZWdpb24gTWV0cm9wb2xpdGFuYTEnMCUGA1UECxQe
' U2VydmljaW8gZGUgSW1wdWVzdG9zIEludGVybm9zMScwJQYDVQQKFB5TZXJ2aWNp
' byBkZSBJbXB1ZXN0b3MgSW50ZXJub3MxETAPBgNVBAcUCFNhbnRpYWdvMR8wHQYJ
' KoZIhvcNAQkBFhB3Z29uemFsZXpAc2lpLmNsMSMwIQYDVQQDFBpXaWxpYmFsZG8g
' R29uemFsZXogQ2FicmVyYTELMAkGA1UEBhMCQ0wwXDANBgkqhkiG9w0BAQEFAANL
' ADBIAkEAvNQyaLPd3cQlBr0fQWooAKXSFan/WbaFtD5P7QDzcE1pBIvKY2Uv6uid
' ur/mGVB9IS4Fq/1xRIXy13FFmxLwTQIDAQABo4IBgjCCAX4wIwYDVR0RBBwwGqAY
' BggrBgEEAcNSAaAMFgowNzg4MDQ0Mi00MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6
' Ly9jcmwuZS1jZXJ0Y2hpbGUuY2wvRWNlcnRjaGlsZUNBSS5jcmwwIwYDVR0SBBww
' GqAYBggrBgEEAcEBAqAMFgo5NjkyODE4MC01MIHmBgNVHSAEgd4wgdswgdgGCCsG
' AQQBw1IAMIHLMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3LmUtY2VydGNoaWxlLmNs
' L3BvbGl0aWNhL2Nwcy5odG0wgZAGCCsGAQUFBwICMIGDGoGARWwgdGl0dWxhciBo
' YSBzaWRvIHZhbGlkYWRvIGVuIGZvcm1hIHByZXNlbmNpYWwsIHF1ZWRhbmRvIGhh
' YmlsaXRhZG8gZWwgQ2VydGlmaWNhZG8gcGFyYSB1c28gdHJpYnV0YXJpbywgcGFn
' b3MsIGNvbWVyY2lvIHUgb3Ryb3MwCwYDVR0PBAQDAgTwMAsGCSqGSIb3DQEBBAOB
' gQB2V4cTj7jo1RawmsRQUSnnvJjMCrZstcHY+Ss3IghVPO9eGoYzu5Q63vzt0Pi8
' CS91SBc7xo+LDoljaUyjOzj7zvU7TpWoFndiTQF3aCOtTkV+vjCMWW3sVHes4UCM
' DkF3VYK+rDTAadiaeDArTwsx4eNEpxFuA/TJwcXpLQRCDg==</X509Certificate>

    Dim strCert64 As String
    Dim strXmlKey As String
    
    strCert64 = "MIIEPjCCA6mgAwIBAgIDAgGKMAsGCSqGSIb3DQEBBDCBsTEdMBsGA1UECBQUUmVnaW9uIE1ldHJvcG9saXRhbmExETAPBgNVBAcUCFNhbnRpYWdvMSIwIAYDVQQDFBlFLUNlcnRjaGlsZSBDQSBJbnRlcm1lZGlhMTYwNAYDVQQLFC1FbXByZXNhIE5hY2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWx" & _
"lY3Ryb25pY2ExFDASBgNVBAoUC0UtQ0VSVENISUxFMQswCQYDVQQGEwJDTDAeFw0wMjEwMDIxOTExNTlaFw0wMzEwMDIwMDAwMDBaMIHXMR0wGwYDVQQIFBRSZWdpb24gTWV0cm9wb2xpdGFuYTEnMCUGA1UECxQeU2VydmljaW8gZGUgSW1wdWVzdG9zIEludGVybm9zMScwJQYDVQQKFB5TZXJ2aWNpbyBkZSBJbXB1ZXN" & _
"0b3MgSW50ZXJub3MxETAPBgNVBAcUCFNhbnRpYWdvMR8wHQYJKoZIhvcNAQkBFhB3Z29uemFsZXpAc2lpLmNsMSMwIQYDVQQDFBpXaWxpYmFsZG8gR29uemFsZXogQ2FicmVyYTELMAkGA1UEBhMCQ0wwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAvNQyaLPd3cQlBr0fQWooAKXSFan/WbaFtD5P7QDzcE1pBIvKY2Uv6ui" & _
"dur/mGVB9IS4Fq/1xRIXy13FFmxLwTQIDAQABo4IBgjCCAX4wIwYDVR0RBBwwGqAYBggrBgEEAcNSAaAMFgowNzg4MDQ0Mi00MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuZS1jZXJ0Y2hpbGUuY2wvRWNlcnRjaGlsZUNBSS5jcmwwIwYDVR0SBBwwGqAYBggrBgEEAcEBAqAMFgo5NjkyODE4MC01MIHmBgNVHSA" & _
"Egd4wgdswgdgGCCsGAQQBw1IAMIHLMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3LmUtY2VydGNoaWxlLmNsL3BvbGl0aWNhL2Nwcy5odG0wgZAGCCsGAQUFBwICMIGDGoGARWwgdGl0dWxhciBoYSBzaWRvIHZhbGlkYWRvIGVuIGZvcm1hIHByZXNlbmNpYWwsIHF1ZWRhbmRvIGhhYmlsaXRhZG8gZWwgQ2VydGlmaWNhZG8" & _
"gcGFyYSB1c28gdHJpYnV0YXJpbywgcGFnb3MsIGNvbWVyY2lvIHUgb3Ryb3MwCwYDVR0PBAQDAgTwMAsGCSqGSIb3DQEBBAOBgQB2V4cTj7jo1RawmsRQUSnnvJjMCrZstcHY+Ss3IghVPO9eGoYzu5Q63vzt0Pi8CS91SBc7xo+LDoljaUyjOzj7zvU7TpWoFndiTQF3aCOtTkV+vjCMWW3sVHes4UCMDkF3VYK+rDTAadi" & _
"aeDArTwsx4eNEpxFuA/TJwcXpLQRCDg=="

    strXmlKey = "<RSAKeyValue><Modulus>" & _
        "tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx" & _
        "iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx" & _
        "BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=</Modulus>" & _
        "<Exponent>AQAB</Exponent></RSAKeyValue>"

    Debug.Print IsCertAndKeyValueMatched(strCert64, strXmlKey)
    
End Sub

 Public Function Hash_String_SHA1(strMessage, Optional ShowDebug As Boolean) As String
' $GENERIC-FUNCTION$
' INPUT:  Message as an ANSI string
' OUTPUT: SHA-1 message digest in hex format
    Dim abMessage() As Byte
    Dim nmLen As Long
    Dim strDigest As String
    Dim nRet As Long
    
    ' Convert ANSI text to bytes
    abMessage = StrConv(strMessage, vbFromUnicode)
    nmLen = UBound(abMessage) - LBound(abMessage) + 1
    
    If ShowDebug Then
        Debug.Print "M (ansi):" & vbCrLf & "'" & StrConv(abMessage, vbUnicode) & "'"
        Debug.Print "M (hex):  " & cnvHexStrFromBytes(abMessage)
        Debug.Print "Message is " & nmLen & " bytes long"
    End If
    
    strDigest = String(PKI_SHA1_CHARS, " ")
    nRet = HASH_HexFromBytes(strDigest, Len(strDigest), abMessage(0), nmLen, PKI_HASH_SHA1)
    If nRet > 0 Then
        strDigest = Left$(strDigest, nRet)
    Else
        Debug.Print "FAILED to create message digest"
        strDigest = ""
    End If
    
    Hash_String_SHA1 = strDigest
    
End Function

 Public Function Hash_File_SHA1(strFileName As String) As String
' $GENERIC-FUNCTION$
' INPUT:  Full path to file
' OUTPUT: SHA-1 message digest in hex format
    Dim strDigest As String
    Dim nRet As Long
    
    strDigest = String(PKI_SHA1_CHARS, " ")
    nRet = HASH_HexFromFile(strDigest, Len(strDigest), strFileName, PKI_HASH_SHA1)
    If nRet > 0 Then
        strDigest = Left$(strDigest, nRet)
    Else
        Debug.Print "FAILED to create message digest"
        strDigest = ""
    End If
    
    Hash_File_SHA1 = strDigest
    
End Function

 Public Sub Test_Make_Hash_of_File()

    Debug.Print "DIGEST=" & Hash_File_SHA1("C:\!Data\CryptoSys\Chile\data.canon.ok.txt")
    
End Sub

 Public Function MakeDigestValueOfSignedInfo(strDigestValue64 As String, strReference As String, fIncludeXMLSchema As Boolean, Optional ShowDebug As Boolean) As String
' $GENERIC-FUNCTION$
' INPUT:  DigestValue of data in base64 format
'         Reference as in <Reference URI="#...">
'         Flag to include XMLSchema-instance attribute
' OUTPUT: SHA-1 digest of canonicalized <SignedInfo> element in HEX format
' REMARKS: This is for the specific form with no whitespace before any element

    Dim strSignedInfoCanonic As String
    Dim strDigest As String
    Dim strFirstLine  As String
    
    'SPECIFIC FORM (no whitespace before elements):
    ' <SignedInfo>
    ' <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
    ' <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    ' <Reference URI="#...">
    ' <Transforms>
    ' <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
    ' </Transforms>
    ' <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
    ' <DigestValue>...</DigestValue>
    ' </Reference>
    ' </SignedInfo>
    
    ' To canonicalize the SignedInfo, do the following:-
    ' 1. Replace any CR-LF pairs with single LF char
    ' 2. Add the xmlns attributes to the SignedInfo tag
    ' 3. Convert the empty 'Method' elements to start-end tag pairs
    ' 4. Use double-quotes (") around attribute values
    ' 5. Do NOT change any other whitespace chars outside the tags
    ' -- assumes all other c14n aspects are dealt with in the hard-coding

    ' We can hard-code all of the above directly; well, almost...
    If fIncludeXMLSchema Then
        strFirstLine = "<SignedInfo xmlns=""http://www.w3.org/2000/09/xmldsig#"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">"
    Else
        strFirstLine = "<SignedInfo xmlns=""http://www.w3.org/2000/09/xmldsig#"">"
    End If
    
    strSignedInfoCanonic = _
        strFirstLine & vbLf & _
        "<CanonicalizationMethod Algorithm=""http://www.w3.org/TR/2001/REC-xml-c14n-20010315""></CanonicalizationMethod>" & vbLf & _
        "<SignatureMethod Algorithm=""http://www.w3.org/2000/09/xmldsig#rsa-sha1""></SignatureMethod>" & vbLf & _
        "<Reference URI=""#" & strReference & """>" & vbLf & _
        "<Transforms>" & vbLf & _
        "<Transform Algorithm=""http://www.w3.org/TR/2001/REC-xml-c14n-20010315""></Transform>" & vbLf & _
        "</Transforms>" & vbLf & _
        "<DigestMethod Algorithm=""http://www.w3.org/2000/09/xmldsig#sha1""></DigestMethod>" & vbLf & _
        "<DigestValue>" & strDigestValue64 & "</DigestValue>" & vbLf & _
        "</Reference>" & vbLf & _
        "</SignedInfo>"
        
    If ShowDebug Then
        Debug.Print strSignedInfoCanonic
    End If
    strDigest = Hash_String_SHA1(strSignedInfoCanonic)
    ''Debug.Print strDigest
    MakeDigestValueOfSignedInfo = strDigest
    
End Function

 Public Sub Test_DigestSignedInfo()
    Dim strComputed As String
    Dim strCorrect As String
    
    Debug.Print
    Debug.Print "F60T33 Outer XML-Dsig:"
    strCorrect = "98B466FFE6A5E73B85962BC7300841C5F1693D3A"
    strComputed = MakeDigestValueOfSignedInfo("4OTWXyRl5fw3htjTyZXQtYEsC3E=", "SetDoc", fIncludeXMLSchema:=True)
    Debug.Print "COMPUTED=" & strComputed
    Debug.Print "CORRECT= " & strCorrect

    Debug.Print "F60T33 Inner XML-Dsig:"
    strCorrect = "353CABFE369AAD9BEA21F5FA06CB367960FFB7D4"
    strComputed = MakeDigestValueOfSignedInfo("hlmQtu/AyjUjTDhM3852wvRCr8w=", "F60T33", fIncludeXMLSchema:=False)
    Debug.Print "COMPUTED=" & strComputed
    Debug.Print "CORRECT= " & strCorrect
    
End Sub

 Public Function XMLDSIG_VerifySigUsingRSAKeyValue(strSig64 As String, strXmlKey As String) As String
' $GENERIC-FUNCTION$
' INPUT:  strSig64  = <SignatureValue> in base64 excluding XML tags
'         strXmlKey = <RSAKeyValue> in XMLSIG format including XML tags
' OUTPUT: DigestValue in hex format
    Dim nRet As Long
    Dim abData() As Byte
    Dim nDataLen As Long
    Dim strPublicKey As String
    Dim nKeyLen As Long
    Dim nLen As Long
    Dim abDigest() As Byte
    Dim nDigLen As Long
    
    Debug.Print "SigBase64=" & strSig64
    ' Convert XML key form to our internal format
    nLen = RSA_FromXMLString("", 0, strXmlKey, 0)
    If nLen <= 0 Then
        Debug.Print "ERROR: Unable to read XML key string"
        Exit Function
    End If
    strPublicKey = String(nLen, " ")
    nLen = RSA_FromXMLString(strPublicKey, Len(strPublicKey), strXmlKey, 0)
    Debug.Print "Key length is " & RSA_KeyBits(strPublicKey) & " bits"
    
    ' Convert signatureValue to byte format
    abData = cnvBytesFromB64Str(strSig64)
    Debug.Print "SIGNATURE=" & cnvHexStrFromBytes(abData)
    nDataLen = UBound(abData) - LBound(abData) + 1
    Debug.Print "Signature is " & nDataLen & " bytes long"
    ' Verify the key - this should return 1
    nRet = RSA_CheckKey(strPublicKey, 0)
    Debug.Print "RSA_CheckKey returns " & nRet & " (expecting 1)"
    ' Check the length
    nKeyLen = RSA_KeyBytes(strPublicKey)
    Debug.Print "Key is " & nKeyLen & " bytes long"
    If nKeyLen <> nDataLen Then
        Debug.Print "ERROR: Key is the wrong length!"
        Exit Function
    End If
    ' Decrypt the signature
    nRet = RSA_RawPublic(abData(0), nDataLen, strPublicKey, 0)
    Debug.Print "RSA_RawPublic returns " & nRet & " (expecting 0)"
    If nRet <> 0 Then
        Debug.Print "ERROR: " & pkiErrorLookup(nRet)
        Exit Function
    End If
    Debug.Print "EMSA BLOCK=" & cnvHexStrFromBytes(abData)
    
    ' Extract digest from block
    nDigLen = RSA_DecodeMsg(0, 0, abData(0), nDataLen, PKI_EMSIG_PKCSV1_5)
    Debug.Print "RSA_DecodeMsg returns " & nDigLen & " (expecting 20)"
    If nDigLen <= 0 Then
        Debug.Print "ERROR: Cannot extract digest"
        Exit Function
    End If
    ReDim abDigest(nDigLen - 1)
    nRet = RSA_DecodeMsg(abDigest(0), nDigLen, abData(0), nDataLen, PKI_EMSIG_PKCSV1_5)
    
    ' Display bytes as hex and base64
    Debug.Print "digest=" & cnvHexStrFromBytes(abDigest)
    Debug.Print "base64=" & cnvB64StrFromBytes(abDigest)
    Debug.Print
    ' Return result
    XMLDSIG_VerifySigUsingRSAKeyValue = cnvHexStrFromBytes(abDigest)
    
End Function

 Public Function XMLDSIG_VerifySigUsingCert(strSig64 As String, strCert64 As String) As String
' $GENERIC-FUNCTION$
' INPUT:  strSig64  = <SignatureValue> content in base64 excluding tags
'         strCert64 = <X509Certificate> content in base64 excluding tags
' OUTPUT: digestValue in hex format
    Dim abData() As Byte
    Dim strPublicKey As String
    Dim nRet As Long
    Dim nDataLen As Long
    Dim nKeyLen As Long
    Dim nChars As Long
    Dim abDigest() As Byte
    Dim nDigLen As Long
    Dim strQuery As String
    Dim strOutput As String
    Dim strXmlKey As String
    Dim nLen As Long
    
    
    strOutput = String(512, " ")
    ' Query the certificate for details
    strQuery = "subjectName"
    nRet = X509_QueryCert(strOutput, Len(strOutput), strCert64, strQuery, 0)
    If nRet <= 0 Then
        Debug.Print "ERROR: Error " & nRet & " reading certificate"
        Exit Function ' ERROR
    End If
    Debug.Print strQuery & "=" & Left(strOutput, nRet)

    ' Extract public key from X.509 certificate in internal string form
    nChars = RSA_GetPublicKeyFromCert("", 0, strCert64, 0)
    Debug.Print "Chars in internal key = " & nChars
    If nChars <= 0 Then
        Debug.Print "ERROR: Error reading certificate key"
        Exit Function ' ERROR
    End If
    strPublicKey = String(nChars, " ")
    nChars = RSA_GetPublicKeyFromCert(strPublicKey, Len(strPublicKey), strCert64, 0)
    Debug.Print "PUBLIC KEY (internal)=" & strPublicKey
    
    ' While we're here, display the public key in XML format
    nLen = RSA_ToXMLString("", 0, strPublicKey, 0)
    If nLen > 0 Then
        strXmlKey = String(nLen, " ")
        nLen = RSA_ToXMLString(strXmlKey, Len(strXmlKey), strPublicKey, 0)
        Debug.Print strXmlKey
    Else
        Debug.Print "**Failed to create public key in XML"
    End If
    
    ' Convert signatureValue to byte format
    abData = cnvBytesFromB64Str(strSig64)
    Debug.Print "SIGNATURE=" & cnvHexStrFromBytes(abData)
    nDataLen = UBound(abData) - LBound(abData) + 1
    Debug.Print "Signature is " & nDataLen & " bytes long"
    ' Verify the key - this should return 1
    nRet = RSA_CheckKey(strPublicKey, 0)
    Debug.Print "RSA_CheckKey returns " & nRet & " (expecting 1)"
    ' Check the length
    nKeyLen = RSA_KeyBytes(strPublicKey)
    Debug.Print "Key is " & nKeyLen & " bytes long"
    If nKeyLen <> nDataLen Then
        Debug.Print "ERROR: Key is the wrong length!"
        Exit Function
    End If
    ' Decrypt the signature
    nRet = RSA_RawPublic(abData(0), nDataLen, strPublicKey, 0)
    Debug.Print "RSA_RawPublic returns " & nRet & " (expecting 0)"
    Debug.Print "EMSA BLOCK=" & cnvHexStrFromBytes(abData)
    
    ' Extract digest from block
    nDigLen = RSA_DecodeMsg(0, 0, abData(0), nDataLen, PKI_EMSIG_PKCSV1_5)
    Debug.Print "RSA_DecodeMsg returns " & nDigLen & " (expecting 20)"
    If nDigLen <= 0 Then
        Debug.Print "ERROR: Cannot extract digest"
        Exit Function
    End If
    ReDim abDigest(nDigLen - 1)
    nRet = RSA_DecodeMsg(abDigest(0), nDigLen, abData(0), nDataLen, PKI_EMSIG_PKCSV1_5)
    
    ' Display bytes as hex and base64
    Debug.Print "digest=" & cnvHexStrFromBytes(abDigest)
    Debug.Print "base64=" & cnvB64StrFromBytes(abDigest)
    ' Return digest in hex
    XMLDSIG_VerifySigUsingCert = cnvHexStrFromBytes(abDigest)
End Function

 Public Sub TestReadFRMA()
    Dim strCert64 As String
    Dim strSig64 As String
    Dim strDigestHex As String
    
    '<FRMA algoritmo="SHA1withRSA">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA>
    strSig64 = "g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ=="
    
    ' [2009-05-07] We have the certificate for the signer of this now...
    strCert64 = "-----BEGIN CERTIFICATE-----" & vbCrLf & _
        "MIICTzCCAfmgAwIBAwIBZDANBgkqhkiG9w0BAQQFADCBrzELMAkGA1UEBhMCQ0wx" & _
        "CzAJBgNVBAgTAkNTMQswCQYDVQQHEwJDczEnMCUGA1UEChMeU2VydmljaW8gZGUg" & _
        "SW1wdWVzdG9zIEludGVybm9zMRkwFwYDVQQLExBPZmljaW5hIEludGVybmV0MScw" & _
        "JQYDVQQDEx5TZXJ2aWNpbyBkZSBJbXB1ZXN0b3MgSW50ZXJub3MxGTAXBgkqhkiG" & _
        "9w0BCQEWCnNpaUBzaWkuY2wwHhcNMDMwNDI5MjAxNDA1WhcNMDQwNDIzMjAxNDA1" & _
        "WjCBrzELMAkGA1UEBhMCQ0wxCzAJBgNVBAgTAkNTMQswCQYDVQQHEwJDczEnMCUG" & _
        "A1UEChMeU2VydmljaW8gZGUgSW1wdWVzdG9zIEludGVybm9zMRkwFwYDVQQLExBP" & _
        "ZmljaW5hIEludGVybmV0MScwJQYDVQQDEx5TZXJ2aWNpbyBkZSBJbXB1ZXN0b3Mg" & _
        "SW50ZXJub3MxGTAXBgkqhkiG9w0BCQEWCnNpaUBzaWkuY2wwXDANBgkqhkiG9w0B" & _
        "AQEFAANLADBIAkEAyPvwDcshpVAApYVSLF3lKKc+DOFswDqx5ep95LKigRHjUvrv" & _
        "jct9UeNJq9SxBdzU9nz54TEVBYyfAVQpG4xxUwIDAQABMA0GCSqGSIb3DQEBBAUA" & _
        "A0EADvJX1C7hUFD2Eq9jNZpeJ/YBOZx1SBmAHSeXud6fTw98+AR4X3YDmzO9D4Kd" & _
        "hEFi3NK4anpjiPOKbA8fBWyyBA==" & _
        "-----END CERTIFICATE-----"

    strDigestHex = XMLDSIG_VerifySigUsingCert(strSig64, strCert64)
    
End Sub

 Public Sub MakeFRMAdigest()
' This should give the same digest value we obtained above.
    Dim strData As String
    Dim strDigest As String
    Dim nLen As Long
    ' Put <DA> data into a single string with no spaces between elements
    strData = "<DA><RE>97975000-5</RE><RS>RUT DE PRUEBA</RS><TD>33</TD><RNG><D>1</D><H>200</H></RNG><FA>2003-09-04</FA><RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK><IDK>100</IDK></DA>"
    ' Create an SHA-1 digest value in hex format
    strDigest = String(PKI_SHA1_CHARS, " ")
    nLen = HASH_HexFromString(strDigest, Len(strDigest), strData, Len(strData), PKI_HASH_SHA1)
    Debug.Print "SHA1(DA)=" & strDigest
    ' Display in base64
    Debug.Print "SHA1(DA)=" & cnvB64StrFromBytes(cnvBytesFromHexStr(strDigest))
End Sub

 Public Sub TestReadFRMT()
    Dim strXmlKey As String
    Dim strSig64 As String
    
    ' <FRMT algoritmo="SHA1withRSA">GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8iS4HXvJj3oYZuey53Krniew==</FRMT>
    strSig64 = "GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8iS4HXvJj3oYZuey53Krniew=="
    
    ' Form XML string of public key in proper XMLSIG format using <RSAPK> element in XML file
    ' <RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK>
    strXmlKey = "<RSAKeyValue><Modulus>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</Modulus>" & _
    "<Exponent>Aw==</Exponent></RSAKeyValue>"
    
    Call XMLDSIG_VerifySigUsingRSAKeyValue(strSig64, strXmlKey)
    
End Sub

 Public Sub TestF60T33OuterXmlSigByCert()
' THIS DOES NOT WORK! Because the X509 Certificate provided does not correspond to the key used to make the signature.
    Dim strSig64 As String
    Dim strCert64 As String
    
    strCert64 = "MIIEgjCCA+ugAwIBAgIEAQAApzANBgkqhkiG9w0BAQUFADCBtTELMAkGA1UEBhMCQ0wxHTAbBgNVBAgUFFJlZ2lvbiBNZXRyb3BvbGl0YW5hMREwDwYDVQQHFAhTYW50aWFnbzEUMBIGA1UEChQLRS1DRVJUQ0hJTEUxIDAeBgNVBAsUF0F1dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhMRcwFQYDVQQDFA5FLUNFUlRDSElMRSBD" & _
"QTEjMCEGCSqGSIb3DQEJARYUZW1haWxAZS1jZXJ0Y2hpbGUuY2wwHhcNMDMxMDAxMTg1ODE1WhcNMDQwOTMwMDAwMDAwWjCBuDELMAkGA1UEBhMCQ0wxFjAUBgNVBAgUDU1ldHJvcG9saXRhbmExETAPBgNVBAcUCFNhbnRpYWdvMScwJQYDVQQKFB5TZXJ2aWNpbyBkZSBJbXB1ZXN0b3MgSW50ZXJub3MxDzANBgNVBAsU" & _
"BlBpc28gNDEjMCEGA1UEAxQaV2lsaWJhbGRvIEdvbnphbGV6IENhYnJlcmExHzAdBgkqhkiG9w0BCQEWEHdnb256YWxlekBzaWkuY2wwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALxZlVh1xr9sKQIBDF/6Va+lsHQSG5AAmCWvtNTIOXN3E9EQCy7pOPHrDg6EusvoHyesZSKJbc0TnIFXZp78q7mbdHijzKqvMmyv" & _
"wbdP7KK8LQfwf84W4v9O8MJeUHlbJGlo5nFACrPAeTtONbHaReyzeMDv2EganNEDJc9c+UNfAgMBAAGjggGYMIIBlDAjBgNVHREEHDAaoBgGCCsGAQQBwQEBoAwWCjA3ODgwNDQyLTQwCQYDVR0TBAIwADA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmUtY2VydGNoaWxlLmNsL2UtY2VydGNoaWxlY2EuY3JsMCMG" & _
"A1UdEgQcMBqgGAYIKwYBBAHBAQKgDBYKOTY5MjgxODAtNTAfBgNVHSMEGDAWgBTgKP3S4GBPs0brGsz1CJEHcjodCDCB0AYDVR0gBIHIMIHFMIHCBggrBgEEAcNSBTCBtTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5lLWNlcnRjaGlsZS5jbC8yMDAwL0NQUy8wgYEGCCsGAQUFBwICMHUac0VsIHRpdHVsYXIgaGEgc2lk" & _
"byB2YWxpZG8gZW4gZm9ybWEgcHJlc2VuY2lhbCwgcXVlZGFuZG8gZWwgQ2VydGlmaWNhZG8gcGFyYSB1c28gdHJpYnV0YXJpbywgcGFnb3MsIGNvbWVyY2lvIHkgb3Ryb3MwCwYDVR0PBAQDAgTwMA0GCSqGSIb3DQEBBQUAA4GBABMfCyJF0mNXcov8iEWvjGFyyPTsXwvsYbbkOJ41wjaGOFMCInb4WY0ngM8BsDV22bGM" & _
"s8oLyX7rVy16bGA8Z7WDUtYhoOM7mqXw/Hrpqjh3JgAf8zqdzBdH/q6mAbdvq/yb04JHKWPC7fMFuBoeyVWAnhmuMZfReWQiMUEHGGIW"
    strSig64 = "sBnr8Yq14vVAcrN/pKLD/BrqUFczKMW3y1t3JOrdsxhhq6IxvS13SgyMXbIN/T9ciRaFgNabs3pi732XhcpeiSmD1ktzbRctEbSIszYkFJY49k0eB+TVzq3eVaQr4INrymfuOnWj78BZcwKuXvDy4iAcx6/TBbAAkPFwMP9ql2s="

    Call XMLDSIG_VerifySigUsingCert(strSig64, strCert64)

End Sub

 Public Sub TestF60T33OuterXmlSigByRSA()
' THIS WORKS...
    Dim strSig64 As String
    Dim strXmlKey As String
    
    strXmlKey = "<RSAKeyValue><Modulus>" & _
        "tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx" & _
        "iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx" & _
        "BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=</Modulus>" & _
        "<Exponent>AQAB</Exponent></RSAKeyValue>"
    strSig64 = "sBnr8Yq14vVAcrN/pKLD/BrqUFczKMW3y1t3JOrdsxhhq6IxvS13SgyMXbIN/T9ciRaFgNabs3pi732XhcpeiSmD1ktzbRctEbSIszYkFJY49k0eB+TVzq3eVaQr4INrymfuOnWj78BZcwKuXvDy4iAcx6/TBbAAkPFwMP9ql2s="

    Call XMLDSIG_VerifySigUsingRSAKeyValue(strSig64, strXmlKey)

End Sub

 Public Sub TestF60T33InnerXmlSigByRSA()
' THIS WORKS...
    Dim strSig64 As String
    Dim strXmlKey As String
    
    strXmlKey = "<RSAKeyValue><Modulus>" & _
        "tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx" & _
        "iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx" & _
        "BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=</Modulus>" & _
        "<Exponent>AQAB</Exponent></RSAKeyValue>"
    strSig64 = "JG1Ig0pvSIH85kIKGRZUjkyX6CNaY08Y94j4UegTgDe8+wl61GzqjdR1rfOK9BGn93AMOo6aiAgolW0k/XklNVtM/ZzpNNS3d/fYVa1q509mAMSXbelxSM3bjoa7H6Wzd/mV1PpQ8zK5gw7mgMMP4IKxHyS92G81GEguSmzcQmA="

    Call XMLDSIG_VerifySigUsingRSAKeyValue(strSig64, strXmlKey)

End Sub

 Public Sub TestF60T33FRMTSigByRSA()
    Dim strSig64 As String
    Dim strXmlKey As String
    
    strXmlKey = "<RSAKeyValue><Modulus>" & _
        "0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</Modulus>" & _
        "<Exponent>Aw==</Exponent></RSAKeyValue>"
    strSig64 = "GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8iS4HXvJj3oYZuey53Krniew==="

    Call XMLDSIG_VerifySigUsingRSAKeyValue(strSig64, strXmlKey)

End Sub