Imports System.Text
Imports CryptoSysPKI
' $Id: MexicoSAT.vb $
' $Date: 2009-09-03 17:09 $
' $Author: dai $
' This module uses functions from the CryptoSys (tm) PKI Toolkit available from
' <www.cryptosys.net/pki/>.
' Include the line "Imports CryptoSysPKI" in your project.
' *************************** COPYRIGHT NOTICE ******************************
' This code was originally written by David Ireland and is copyright
' (C) 2005-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 **************************
Module MexicoSAT
Sub Main()
Call Mex_CreateSignature()
Call Mex_ExtractDigestFromSignature()
Call Mex_CreateDigestFromString()
Call Mex_CheckKeyAndCertMatch()
Call Mex_Convert_Latin1_To_UTF8()
Call Mex_ValidateCert()
Call Mex_CertToBase64String()
Call Mex_SAT_SerialNumber()
Call Mex_QueryCertString()
End Sub
Public Sub Mex_CreateSignature()
'@Proc: Mex_CreateSignature
'@Lang: VB.NET
'@ Creates a signature (sello) value in base64 format given the piped-string input and the private key
Dim strDataFile As String
Dim strKeyFile As String
Dim strPassword As String
Dim sbPrivateKey As New StringBuilder
Dim abDigest() As Byte
Dim abBlock() As Byte
Dim nBlockLen As Integer
''Dim nLen As Integer
''Dim nRet As Integer
Dim strBase64 As String
' INPUT: File containing piped-string formed from XML doc in UTF-8 format (NOTE: no Unicode markers in the file),
' private key file and its secret password(!)
strDataFile = "Muestra-v2_PipedString-UTF8.txt"
strKeyFile = "aaa010101aaa_CSD_01.key"
' Test password - CAUTION: DO NOT hardcode production passwords!
strPassword = "a0123456789"
' 1. Form the message digest hash of the piped-string directly from the file
abDigest = Hash.BytesFromFile(strDataFile, HashAlgorithm.Md5)
If abDigest.Length <= 0 Then
Console.WriteLine("ERROR: Failed to create hash of file.")
Exit Sub
End If
' Display in hex
Console.WriteLine("Digest=" & Cnv.ToHex(abDigest))
' 2. Sign the message digest using the private key
' 2.1 Read in private key from encrypted .key file
sbPrivateKey = Rsa.ReadEncPrivateKey(strKeyFile, strPassword)
If sbPrivateKey.Length = 0 Then
Console.WriteLine("ERROR: Failed to read private key")
Exit Sub
End If
' -- show we got something
Console.WriteLine("Private key is " & Rsa.KeyBits(sbPrivateKey.ToString) & " bits long")
' 2.2 Encode the digest ready for signing with `Encoded Message for Signature' block using PKCS#1 v1.5 method
nBlockLen = Rsa.KeyBytes(sbPrivateKey.ToString)
abBlock = Rsa.EncodeDigestForSignature(nBlockLen, abDigest, HashAlgorithm.Md5)
If abBlock.Length = 0 Then
Console.WriteLine("ERROR with Rsa.EncodeDigestForSignature")
Exit Sub
End If
Console.WriteLine("INPUT BLOCK= " & Cnv.ToHex(abBlock))
' 2.3 Sign using the RSA private key
abBlock = Rsa.RawPrivate(abBlock, sbPrivateKey.ToString)
' Display in hex
Console.WriteLine("OUTPUT BLOCK=" & Cnv.ToHex(abBlock))
' 2.4 Clean up
Wipe.String(sbPrivateKey)
' 3. Convert to base64 and output result
strBase64 = System.Convert.ToBase64String(abBlock)
Console.WriteLine("SIGNATURE VALUE=" & strBase64)
End Sub
Public Function Mex_ExtractDigestFromSignature() As String
'@Proc: Mex_ExtractDigestFromSignature
'@Lang: VB6
'@ Extracts the message digest from a signature (sello) string using the X.509 certificate (certificado) value
Dim sbPublicKey As StringBuilder
Dim strSello As String
Dim strCertificado As String
Dim abDigest() As Byte
Dim abData() As Byte
Dim nSigLen As Integer
Dim strDigestHex As String
Mex_ExtractDigestFromSignature = ""
' INPUT: Base64 strings extracted from the XML file (Ref: Muestra_v2_signed2.xml)
strSello = "UlUSwGNEicfigV6i4RhTy0eb2RYWFYyFatJFcM/u5Wlkb5XRxXiCizTGw5Yxz9oZNk8msAgO4C5Gevjh+S2TJPZueYhaQeZlo6k0rE3CQexkOGVRpHkvAoAgOM5kGKzYe24DKZbTgjNL+ai+tbhEHmRAFcpv2rDpehbL3w6BnYU="
strCertificado = "MIIDhDCCAmygAwIBAgIUMTAwMDEyMDAwMDAwMDAwMjI1MTcwDQYJKoZIhvcNAQEFBQAwgcMxGTAXBgNVBAcTEENpdWRhZCBkZSBNZXhpY28xFTATBgNVBAgTDE1leGljbywgRC5GLjELMAkGA1UEBhMCTVgxGjAYBgNVBAMTEUFDIGRlIFBydWViYXMgU0FUMTYwNAYDVQQLFC1BZG1pbmlzdHJhY2nzbiBkZSBTZWd1cmlkYWQgZGUgbGEgSW5" & _
"mb3JtYWNp824xLjAsBgNVBAoUJVNlcnZpY2lvIGRlIEFkbWluaXN0cmFjafNuIFRyaWJ1dGFyaWEwHhcNMDgwODIxMTUyMjA4WhcNMTAwODIxMTUyMjA4WjCBmDElMCMGA1UELRMcQUFBMDEwMTAxQUFBIC8gQUFBQTAxMDEwMUFBQTEeMBwGA1UEBRMVIC8gQUFBQTAxMDEwMUhERlJYWDAxMRIwEAYDVQQKEwlNYXRyaXogU0ExEzARBgNVBA" & _
"sTClVuaWRhZCAxMCAxEjAQBgNVBAMTCU1hdHJpeiBTQTESMBAGA1UEKRMJTWF0cml6IFNBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpmiW1q9gyzCFtMcbaFDJexk2IpLoTdNXg4ToGRZ/f+hIjmj3N6ODWX1ARNFGYocEHf113GpW5Oe/mj6UqhBpiH4JRTNR4Udb8myJTArIlODynVHuIUuyhKo7gbMbDdXjilTAYY2XWQuQ7aDtWw" & _
"ntUmNg4vAC/F3OtRz3+y9wM5QIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQUFAAOCAQEAafyD4gMsOvq7E3raPntmQlJTxpWwNySqskE7fe23HVL9UKFCUlWWx/W8gluxIX9S19y17iWnGbtmbNddHxG5PznPsy/a8PlwNHjDW0FOpia2LsvDrNcdPiJhzL/1OVagkenffFf8bLEetF3ktxZ7ifcH1yxV" & _
"xpZ7PS/pe8YIOpWRuMmTV4ypGdsw9TW3HVP5IJ/canuQGPTb3LQ8ojihW2dHnC6ojaWW4GHFSZAPhQJ/DaH/UgFjaQke/RBtoAketfROdG+1qYeA1q/is04O4AXNmMByGp7ZnvGNrO9LDBvs3eKN4ZYcQyjxFEbr1X/xUqHCRF1VEkkC5jJQ1ktC4g=="
' 1. Read in Public key from X.509 certificate string directly
sbPublicKey = Rsa.GetPublicKeyFromCert(strCertificado)
If sbPublicKey.Length = 0 Then
Console.WriteLine("ERROR: failed to read Certificado string")
Exit Function
End If
' --Show we got something useful
Console.WriteLine("Public key is " & Rsa.KeyBits(sbPublicKey.ToString) & " bits long")
' 2. Convert base64 signature value to byte array
abData = System.Convert.FromBase64String(strSello)
nSigLen = abData.Length
' 2a. Check lengths match
Console.WriteLine("Signature bytes=" & nSigLen)
Console.WriteLine("Key bytes =" & Rsa.KeyBytes(sbPublicKey.ToString))
If nSigLen <> Rsa.KeyBytes(sbPublicKey.ToString) Then
Console.WriteLine("ERROR: key length does not match signature")
Exit Function
End If
' 3.Decrypt using RSA public key
abData = Rsa.RawPublic(abData, sbPublicKey.ToString)
Console.WriteLine("RSA_RawPublic returns " & abData.Length & " bytes (expected >0)")
If abData.Length = 0 Then
Console.WriteLine("ERROR: failed to decrypt RSA signature.")
Exit Function
End If
' Display result in hex
Console.WriteLine("Decrypted signature=" & vbCrLf & Cnv.ToHex(abData))
' 4. Decode to extract the original message digest
abDigest = Rsa.DecodeDigestForSignature(abData)
' 5. Convert to hex format
strDigestHex = Cnv.ToHex(abDigest)
' OUTPUT: Digest in hex format
Console.WriteLine("MD5 digest as hex: " & strDigestHex)
Mex_ExtractDigestFromSignature = LCase(Cnv.ToHex(abDigest))
End Function
Public Function Mex_CreateDigestFromString() As String
'@Proc: Mex_CreateDigestFromString
'@Lang: VB.NET
'@ Creates the MD5 digest of the input string after converting to UTF-8 encoding
Dim strData As String
Dim abDataUTF8 As Byte()
Dim strDigest As String
' INPUT: Our original string data in "Latin-1" encoding
strData = "||2.0|A|1|2009-08-16T16:30:00|1|2009|ingreso|Una sola exhibición|350.00|5.25|397.25|ISP900909Q88|Industrias del Sur Poniente, S.A. de C.V.|Alvaro Obregón|37|3|Col. Roma Norte|México|Cuauhtémoc|Distrito Federal|México|06700|Pino Suarez|23|Centro|Monterrey|Monterrey|Nuevo Léon|México|95460|CAUR390312S87|Rosa María Calderón Uriegas|Topochico|52|Jardines del Valle|Monterrey|Monterrey|Nuevo León|México|95465|10|Caja|Vasos decorados|20.00|200|1|pieza|Charola metálica|150.00|150|IVA|15.00|52.50||"
Console.WriteLine("INPUT=" & strData)
' 1. Convert to UTF-8
' -- In VB.NET, it is easier to convert directly to a byte array
abDataUTF8 = System.Text.Encoding.UTF8.GetBytes(strData)
' 2. Create the message digest hash
strDigest = Hash.HexFromBytes(abDataUTF8, HashAlgorithm.Md5)
' OUTPUT: Display digest in hex format
Console.WriteLine("Digest=" & strDigest)
Mex_CreateDigestFromString = strDigest
End Function
Public Sub Mex_CheckKeyAndCertMatch()
'@Proc: Mex_Create_Digest
'@Lang: VB.NET
'@ Checks that the keys in the private key and certificate match
Dim strCertFile As String
Dim strKeyFile As String
Dim sbPassword As New StringBuilder
Dim sbPublicKey As New StringBuilder
Dim sbPrivateKey As New StringBuilder
Dim nRet As Integer
' INPUT: filenames for certificate and private key files
strCertFile = "aaa010101aaa_CSD_01.cer"
strKeyFile = "aaa010101aaa_CSD_01.key"
' Test password - CAUTION: DO NOT hardcode production passwords!
sbPassword.Append("a0123456789")
' 1. Read in private key from encrypted .key file
sbPrivateKey = Rsa.ReadEncPrivateKey(strKeyFile, sbPassword.ToString)
If sbPrivateKey.Length > 0 Then
Console.WriteLine("Private key is " & Rsa.KeyBits(sbPrivateKey.ToString) & " bits")
Else
Console.WriteLine("ERROR: Cannot read private key file.")
Exit Sub
End If
' 2. Clean up password as we are done with it
Wipe.String(sbPassword)
' 3. Read in public key from certificate
sbPublicKey = Rsa.GetPublicKeyFromCert(strCertFile)
If sbPublicKey.Length > 0 Then
Console.WriteLine("Public key is " & Rsa.KeyBits(sbPublicKey.ToString) & " bits")
Else
Console.WriteLine("ERROR: Cannot read certificate file.")
Exit Sub
End If
' 4. See if the two key strings match
nRet = Rsa.KeyMatch(sbPrivateKey.ToString, sbPublicKey.ToString)
If nRet = 0 Then
Console.WriteLine("OK, key strings match.")
Else
Console.WriteLine("FAILED: key strings do not match.")
End If
' 5. Clean up private key string
Wipe.String(sbPrivateKey)
End Sub
Public Sub Mex_Convert_Latin1_To_UTF8()
'@Proc: Mex_Convert_Latin1_To_UTF8
'@Lang: VB.NET $
'@ Checks if a string is valid UTF-8 and converts between Latin-1 and UTF-8 encodings
Dim strData As String
Dim strDataUTF8 As String
Dim nRet As Integer
' Our original string data is in "Latin-1" encoding
strData = "Asociación Mexicana de Estándares para el Comercio Electrónico A.C.|México|"
Console.WriteLine("INPUT: " & strData)
' Is it valid UTF-8?
nRet = Cnv.CheckUTF8(strData)
Console.WriteLine("CNV.CheckUTF8 returns " & nRet & " (0 => Not valid UTF-8)")
' So convert to UTF-8: use the standard method in .NET
strDataUTF8 = System.Text.Encoding.Default.GetString(System.Text.Encoding.UTF8.GetBytes(strData))
' Which may not display correctly ...!
Console.WriteLine("UTF-8: " & strDataUTF8)
nRet = Cnv.CheckUTF8(strDataUTF8)
Console.WriteLine("CNV.CheckUTF8 returns " & nRet & " (1,2,3 => Valid UTF-8)")
End Sub
Public Sub Mex_ValidateCert()
'@Proc: Mex_ValidateCert
'@Lang: VB.NET
'@ Checks that a given X.509 certificate really was issued by the issuer and has not expired
Dim strCert As String
Dim strIssuerCert As String
Dim nRet As Integer
Dim fIsValid As Boolean
' INPUT: Filenames of certificate to be checked and issuer's certificate
strCert = "aaa010101aaa_CSD_01.cer"
strIssuerCert = "AC_SAT2048.cer"
' 1. Was this cert signed by the purported issuer?
nRet = X509.VerifyCert(strCert, strIssuerCert)
Console.WriteLine("X509_VerifyCert returns " & nRet)
If nRet < 0 Then
Console.WriteLine("ERROR: Validation failed")
ElseIf nRet > 0 Then
Console.WriteLine("ERROR: " & General.ErrorLookup(nRet))
Else
Console.WriteLine("OK, cert was signed by issuer.")
End If
' 2. Is this cert still valid now?
fIsValid = X509.CertIsValidNow(strCert)
Console.WriteLine("X509_CertIsValidNow returns " & fIsValid)
If Not fIsValid Then
Console.WriteLine("ERROR: cert has expired")
Else
Console.WriteLine("OK, cert is still valid now.")
End If
End Sub
Public Sub Mex_CertToBase64String()
'@Proc: Mex_CertToBase64String
'@Lang: VB.NET
'@ Converts an X.509 certificate file into a base64 string suitable for Certificado field in XML,
' and shows how this string form can be treated just like the .cer file.
Dim strCertString As String
Dim strCertFile As String
Dim strThumb1 As String
Dim strThumb2 As String
strCertFile = "aaa010101aaa_CSD_01.cer"
' Read in certificate file's data to a string
strCertString = X509.ReadStringFromFile(strCertFile)
Console.WriteLine("For certificate '" & strCertFile & "':")
Console.WriteLine(strCertString)
' Check that the two versions of the certificate are identical by computing their SHA-1 thumbprints
strThumb1 = X509.CertThumb(strCertFile, HashAlgorithm.Sha1)
strThumb2 = X509.CertThumb(strCertString, HashAlgorithm.Sha1)
Console.WriteLine("SHA-1(file) =" & strThumb1)
Console.WriteLine("SHA-1(string)=" & strThumb2)
If strThumb1 = strThumb2 Then
Console.WriteLine("Certificates are identical")
Else
Console.WriteLine("ERROR: certificates do not match")
End If
End Sub
Public Sub Mex_SAT_SerialNumber()
'@Proc: Mex_SAT_SerialNumber
'@Lang: VB.NET
'@ Extracts the serial number from a SAT-issued X.509 certificate and displays in base64 format
Dim strCertFile As String
Dim strSerialNumber As String
Dim strSerialSAT As String
' Extract the certificate's serial number
strCertFile = "AAA010101AAAsd.cer"
strSerialNumber = X509.CertSerialNumber(strCertFile)
Console.WriteLine("X.509 Serial Number=0x" & strSerialNumber)
' Decode from hex-encoded integer to string of ASCII digits
strSerialSAT = System.Text.Encoding.Default.GetString(Cnv.FromHex(strSerialNumber))
Console.WriteLine("Decoded SAT Format ='" & strSerialSAT & "'")
End Sub
Public Sub Mex_QueryCertString()
'@Proc: Mex_QueryCertString
'@Lang: VB.NET
'@ Extracts various details from a certificate string
Dim strCertificado As String
Dim strOutput As String
Dim strQuery As String
' INPUT: Certificado string frm XML file. This is the same as in the file aaa010101aaa_CSD_01.cer.
strCertificado = "MIIDhDCCAmygAwIBAgIUMTAwMDEyMDAwMDAwMDAwMjI1MTcwDQYJKoZIhvcNAQEFBQAwgcMxGTAXBgNVBAcTEENpdWRhZCBkZSBNZXhpY28xFTATBgNVBAgTDE1leGljbywgRC5GLjELMAkGA1UEBhMCTVgxGjAYBgNVBAMTEUFDIGRlIFBydWViYXMgU0FUMTYwNAYDVQQLFC1BZG1pbmlzdHJhY2nzbiBkZSBTZWd1cmlkYWQgZGUgbGEgSW5" & _
"mb3JtYWNp824xLjAsBgNVBAoUJVNlcnZpY2lvIGRlIEFkbWluaXN0cmFjafNuIFRyaWJ1dGFyaWEwHhcNMDgwODIxMTUyMjA4WhcNMTAwODIxMTUyMjA4WjCBmDElMCMGA1UELRMcQUFBMDEwMTAxQUFBIC8gQUFBQTAxMDEwMUFBQTEeMBwGA1UEBRMVIC8gQUFBQTAxMDEwMUhERlJYWDAxMRIwEAYDVQQKEwlNYXRyaXogU0ExEzARBgNVBA" & _
"sTClVuaWRhZCAxMCAxEjAQBgNVBAMTCU1hdHJpeiBTQTESMBAGA1UEKRMJTWF0cml6IFNBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpmiW1q9gyzCFtMcbaFDJexk2IpLoTdNXg4ToGRZ/f+hIjmj3N6ODWX1ARNFGYocEHf113GpW5Oe/mj6UqhBpiH4JRTNR4Udb8myJTArIlODynVHuIUuyhKo7gbMbDdXjilTAYY2XWQuQ7aDtWw" & _
"ntUmNg4vAC/F3OtRz3+y9wM5QIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQUFAAOCAQEAafyD4gMsOvq7E3raPntmQlJTxpWwNySqskE7fe23HVL9UKFCUlWWx/W8gluxIX9S19y17iWnGbtmbNddHxG5PznPsy/a8PlwNHjDW0FOpia2LsvDrNcdPiJhzL/1OVagkenffFf8bLEetF3ktxZ7ifcH1yxV" & _
"xpZ7PS/pe8YIOpWRuMmTV4ypGdsw9TW3HVP5IJ/canuQGPTb3LQ8ojihW2dHnC6ojaWW4GHFSZAPhQJ/DaH/UgFjaQke/RBtoAketfROdG+1qYeA1q/is04O4AXNmMByGp7ZnvGNrO9LDBvs3eKN4ZYcQyjxFEbr1X/xUqHCRF1VEkkC5jJQ1ktC4g=="
' 1. Get the Issuer's distinguished name
strOutput = X509.CertIssuerName(strCertificado, ";")
Console.WriteLine("ISSUER= [" & strOutput & "]")
' 2. Get the Subject's distinguished name
strOutput = X509.CertSubjectName(strCertificado, ";")
Console.WriteLine("SUBJECT=[" & strOutput & "]")
' 3. Get the Serial Number
strOutput = X509.CertSerialNumber(strCertificado)
Console.WriteLine("X.509 Serial Number=0x" & strOutput)
' 4. Get the expiry date
strOutput = X509.CertExpiresOn(strCertificado)
Console.WriteLine("Expires on: " & strOutput)
' 5. Get the signature algorithm
strQuery = "signatureAlgorithm"
strOutput = X509.QueryCert(strCertificado, strQuery)
Console.WriteLine(strQuery & "=" & strOutput)
End Sub
End Module