Imports System Imports System.Diagnostics Imports System.Reflection Imports System.IO Imports System.Text Imports CryptoSysPKI ' Some tests using the CryptoSysPKI .NET interface. ' $Id: TestPKIvbnet.vb $ ' * Last updated: ' * $Date: 2012-01-14 12:09:00 $ ' * $Version: 3.8.0 $ ' ' This is a Console Application written for target .NET Framework 2.0 and above ' NOTE: This program requires the following files to exist in the ' * same directory as the executable (these are in pkiDotNetTestFiles.zip): ' * CarlRSASelf.cer ' * CarlRSASign.epk ' * AliceRSASignByCarl.cer ' * AlicePrivRSASign.epk ' * BobRSASignByCarl.cer ' * BobPrivRSAEncrypt.epk (NOTE: the .EPK extension is one we made up) ' * bob.p7b ' * rfc3280bis_cert1.cer ' * rfc3280bis_cert2.cer ' * rfc3280bis_CRL.crl ' * dims.cer ' * UTNUSERFirst-Object.cer ' * ocsp_response_ok_dims.dat ' Namespace TestPKIExamples ''' <summary> ''' Test examples for CryptoSysPKI interface ''' </summary> Class TestPKIExamples ''' <summary> ''' The main entry point for the application. ''' </summary> <STAThread()> _ Public Shared Sub Main(ByVal args As String()) Dim s As String Dim ch As Char Dim i As Integer, n As Integer, nblock As Integer, r As Integer, k As Integer Dim b As Byte() Dim isok As Boolean Dim msg As Byte() Dim bcheck As Byte() Dim sbPrivateKey As StringBuilder Dim sbPublicKey As StringBuilder Dim keyhex As String, plainStr As String, cipherStr As String, ivhex As String, okhex As String Dim arrPlain As Byte() Dim arrCipher As Byte() Dim arrKey As Byte() Dim arrIV As Byte() Dim excontent As String, fnameData As String, fnameEnc As String, fnameCheck As String Dim fnameInput As String, fnameOutput As String, fnameCert As String, fname As String Dim pubkeyFile As String, prikeyFile As String Dim issuerCert As String Dim hexDigest As String Dim strCheck As String, certBase64 As String, distname As String Dim query As String Dim sbKeyCheck As StringBuilder Dim cert1 As String, cert2 As String, certList As String, certFile As String, crlFile As String, snStr As String, _ csrFile As String Dim extns As String, dn As String, password As String, xmlKey As String, msgStr As String, keyStr As String, _ dateStr As String Dim keyBits As Integer Dim subdir As String Dim b64str As String, s1 As String Dim b1 As Byte() Dim flen As Long ' Required test files Dim arrFileNames As String() = New String() {"AlicePrivRSASign.epk", "AliceRSASignByCarl.cer", "bob.p7b", "BobPrivRSAEncrypt.epk", "BobRSASignByCarl.cer", "CarlPrivRSASign.epk", _ "CarlRSASelf.cer", "rfc3280bis_cert1.cer", "rfc3280bis_cert2.cer", "rfc3280bis_CRL.crl", "dims.cer", "UTNUSERFirst-Object.cer", _ "ocsp_response_ok_dims.dat"} ' Handle arguments Dim doPrompt As Boolean = False Dim askDelete As Boolean = False Dim doBigFile As Boolean = False For iarg As Integer = 0 To args.Length - 1 If args(iarg) = "prompt" Then doPrompt = True End If If args(iarg) = "askdelete" Then askDelete = True End If If args(iarg) = "bigfile" Then doBigFile = True End If Next '**************** ' GENERAL TESTS * '**************** Console.WriteLine("GENERAL FUNCTIONS:") n = CryptoSysPKI.General.Version() Console.WriteLine("Version={0}", n) ch = CryptoSysPKI.General.LicenceType() Console.WriteLine("LicenceType={0}", ch) s = CryptoSysPKI.General.ModuleName() Console.WriteLine("ModuleName={0}", s) s = CryptoSysPKI.General.CompileTime() Console.WriteLine("CompileTime={0}", s) n = General.IsWin64() Console.WriteLine("IsWin64={0}", n) s = General.Platform() Console.WriteLine("Platform={0}", s) s = CryptoSysPKI.General.LastError() Console.WriteLine("LastError='{0}' (expecting empty)", s) n = CryptoSysPKI.General.PowerUpTests() Console.WriteLine("PowerUpTests={0} (expecting 0)", n) '************************************************** ' Check we have required files in local directory * '************************************************** Dim assemblyFile As String = Assembly.GetExecutingAssembly().Location Dim assemblyDir As String = Path.GetDirectoryName(assemblyFile) Console.WriteLine("Local directory is '{0}'.", assemblyDir) Console.WriteLine("Checking required test files are in local directory...") Dim missingFile As String = "STOPPED: Required file is missing." & vbLf & " Look in pkiDotNetTestFiles.zip" For Each fn As String In arrFileNames If FileIsNotPresent(fn, missingFile) Then Return End If Next '************************************************* ' Create a test sub-directory with a random name, ' copy these test files to it, and work in that sub-directory '************************************************* subdir = "pkitest." & Cnv.ToHex(Rng.Bytes(4)) Console.WriteLine("Creating test sub-directory '{0}'", subdir) System.IO.Directory.CreateDirectory(subdir) ' Copy test files For Each fn As String In arrFileNames System.IO.File.Copy(fn, subdir & "\" & fn, True) Next ' Change current working directory to sub-dir System.IO.Directory.SetCurrentDirectory(subdir) Console.WriteLine("CWD is " & System.IO.Directory.GetCurrentDirectory()) ' Now do the tests... '******************************************* ' TDEA (Triple DES, 3DES) ENCRYPTION TESTS * '******************************************* Console.WriteLine("TESTING TRIPLE DES:") keyhex = "010101010101010101010101010101010101010101010101" plainStr = "8000000000000000" cipherStr = "95F8A5E5DD31D900" ' Encrypt in ECB mode using hex strings s = Tdea.Encrypt(plainStr, keyhex, Mode.ECB, Nothing) Console.WriteLine("KY={0}", keyhex) Console.WriteLine("PT={0}", plainStr) Console.WriteLine("CT={0}", s) Console.WriteLine("OK={0}", cipherStr) Debug.Assert([String].Compare(s, cipherStr, True) = 0, "Tdea.HexECB failed") ' Decrypt s = Tdea.Decrypt(cipherStr, keyhex, Mode.ECB, Nothing) Console.WriteLine("P'={0}", s) Console.WriteLine("OK={0}", plainStr) Debug.Assert([String].Compare(s, plainStr, True) = 0, "Tdea.HexECB failed") ' Ditto using byte arrays arrPlain = Cnv.FromHex(plainStr) arrCipher = Cnv.FromHex(cipherStr) arrKey = Cnv.FromHex(keyhex) b = Tdea.Encrypt(arrPlain, arrKey, Mode.ECB, Nothing) Console.WriteLine("CT={0}", Cnv.ToHex(b)) b = Tdea.Decrypt(arrCipher, arrKey, Mode.ECB, Nothing) Console.WriteLine("P'={0}", Cnv.ToHex(b)) ' Encrypt in CBC mode using hex strings keyhex = "737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32" ivhex = "B36B6BFB6231084E" plainStr = "5468697320736F6D652073616D706520636F6E74656E742E0808080808080808" cipherStr = "d76fd1178fbd02f84231f5c1d2a2f74a4159482964f675248254223daf9af8e4" s = Tdea.Encrypt(plainStr, keyhex, Mode.CBC, ivhex) Console.WriteLine("KY={0}", keyhex) Console.WriteLine("IV={0}", ivhex) Console.WriteLine("PT={0}", plainStr) Console.WriteLine("CT={0}", s) Console.WriteLine("OK={0}", cipherStr) Debug.Assert([String].Compare(s, cipherStr, True) = 0, "Tdea.Encrypt{Hex,CBC} failed") ' Decrypt s = Tdea.Decrypt(cipherStr, keyhex, Mode.CBC, ivhex) Console.WriteLine("P'={0}", s) Console.WriteLine("OK={0}", plainStr) Debug.Assert([String].Compare(s, plainStr, True) = 0, "Tdea.Decrypt{Hex,CBC} failed") ' Ditto using byte arrays arrPlain = Cnv.FromHex(plainStr) arrCipher = Cnv.FromHex(cipherStr) arrKey = Cnv.FromHex(keyhex) arrIV = Cnv.FromHex(ivhex) b = Tdea.Encrypt(arrPlain, arrKey, Mode.CBC, arrIV) Console.WriteLine("CT={0}", Cnv.ToHex(b)) b = Tdea.Decrypt(arrCipher, arrKey, Mode.CBC, arrIV) Console.WriteLine("P'={0}", Cnv.ToHex(b)) Debug.Assert([String].Compare(arrPlain.ToString(), b.ToString()) = 0, "Tdea.BytesCBC failed") ' Create a test text file excontent = "This is some sample content." fnameData = "excontent.txt" MakeATextFile(fnameData, excontent) ' Encrypt a file keyhex = "fedcba98765432100123456789abcdeffedcba9876543210" fnameEnc = "excontent.tdea.enc.dat" okhex = "DD1E1FA430AE6BE1D3B83245F7A5B17C4BF03688238778E95F2CCD05AF1A8F44" n = Tdea.FileEncrypt(fnameEnc, fnameData, keyhex, Mode.ECB, Nothing) If 0 = n Then Console.WriteLine("Tdea.File created encrypted file '{0}'", fnameEnc) Else Console.WriteLine("Tdea.File returned error code {0}", n) End If Debug.Assert(0 = n, "Tdea.File failed.") ' Check we got what we should b = ReadABinaryFile(fnameEnc) Console.WriteLine("CT={0}", Cnv.ToHex(b)) Debug.Assert([String].Compare(Cnv.ToHex(b), okhex, True) = 0, "Tdea.FileEncrypt failed") ' Decrypt it using byte format of key instead of hex fnameCheck = "excontent.tdea.chk.txt" b = Cnv.FromHex(keyhex) n = Tdea.FileDecrypt(fnameCheck, fnameEnc, b, Mode.ECB, Nothing) If 0 = n Then Console.WriteLine("Tdea.File decrypted to file '{0}'", fnameCheck) ' Show contents of file s = ReadATextFile(fnameCheck) Debug.Assert([String].Compare(s, excontent) = 0, "Decrypted file data does not match") Else Console.WriteLine("Tdea.File returned error code {0}", n) End If Debug.Assert(0 = n, "Tdea.File failed.") '************ ' RSA TESTS * '************ Console.WriteLine("RSA FUNCTION TESTS:") ' Read the public key from the recipient's X.509 certificate sbPublicKey = Rsa.GetPublicKeyFromCert("AliceRSASignByCarl.cer") Console.WriteLine("PublicKey={0}", sbPublicKey.ToString()) Console.WriteLine("PublicKeyBits={0}", Rsa.KeyBits(sbPublicKey.ToString())) Console.WriteLine("PublicKeyBytes={0}", Rsa.KeyBytes(sbPublicKey.ToString())) ' A message to transmit in byte format msg = System.Text.Encoding.[Default].GetBytes("Hello world!") ' Make an RSA data block of same length in bytes as key ' using EME-PKCS1-v1_5 encoding nblock = Rsa.KeyBytes(sbPublicKey.ToString()) ' [old, deprecated way:] b = Rsa.EncodeMsg(nblock, msg, Rsa.EncodeFor.Encryption); ' [New way in v2.9:] b = Rsa.EncodeMsgForEncryption(nblock, msg, Rsa.EME.PKCSv1_5) Console.WriteLine("BLK={0}", Cnv.ToHex(b)) ' Encrypt with RSA public key b = Rsa.RawPublic(b, sbPublicKey.ToString()) Console.WriteLine("ENC={0}", Cnv.ToHex(b)) ' Read in the private key from the encrypted key file sbPrivateKey = Rsa.ReadEncPrivateKey("AlicePrivRSASign.epk", "password") Console.WriteLine("PrivateKey={0}", sbPrivateKey.ToString()) Console.WriteLine("PrivateKeyBits={0}", Rsa.KeyBits(sbPrivateKey.ToString().ToString())) Console.WriteLine("PrivateKeyBytes={0}", Rsa.KeyBytes(sbPrivateKey.ToString().ToString())) ' Decrypt with private key b = Rsa.RawPrivate(b, sbPrivateKey.ToString().ToString()) Console.WriteLine("DEC={0}", Cnv.ToHex(b)) ' Extract the message from the encryption block ' [old, deprecated way:] b = Rsa.DecodeMsg(b, Rsa.EncodeFor.Encryption); ' [New way in v2.9:] b = Rsa.DecodeMsgForEncryption(b, Rsa.EME.PKCSv1_5) Console.WriteLine("MSG={0}", Cnv.ToHex(b)) ' Convert back to a string s = System.Text.Encoding.[Default].GetString(b) Console.WriteLine("MSG={0}", s) ' Now use to do RSA digital signing ' A message to sign in byte format msg = System.Text.Encoding.[Default].GetBytes("abc") ' Make an RSA data block of same length in bytes as key ' using EMSA-PKCS1-v1_5 encoding with SHA-1 nblock = Rsa.KeyBytes(sbPrivateKey.ToString().ToString()) ' [old, deprecated way:] b = Rsa.EncodeMsg(nblock, msg, Rsa.EncodeFor.Signature); ' [New way in v2.9:] b = Rsa.EncodeMsgForSignature(nblock, msg, HashAlgorithm.Sha1) Console.WriteLine("BLK={0}", Cnv.ToHex(b)) ' Sign with RSA private key b = Rsa.RawPrivate(b, sbPrivateKey.ToString().ToString()) Console.WriteLine("SIG={0}", Cnv.ToHex(b)) ' Transmit the message + SIG block to recipient... ' Decrypt to verify with RSA public key b = Rsa.RawPublic(b, sbPublicKey.ToString()) Console.WriteLine("VER={0}", Cnv.ToHex(b)) ' Create an independent Encoded block from message ' [old, deprecated way:] bcheck = Rsa.EncodeMsg(nblock, msg, Rsa.EncodeFor.Signature); ' [New way in v2.9:] bcheck = Rsa.EncodeMsgForSignature(nblock, msg, HashAlgorithm.Sha1) ' And compare to see if they are the same ' (mmm, C# doesn't have a memcmp function, so we need our own function) If CompareByteArrays(bcheck, b) Then Console.WriteLine("OK, verification OK") Else Console.WriteLine("ERROR: verification failed") End If Debug.Assert(CompareByteArrays(bcheck, b), "Verification failed") ' Test Encode & Decode functions directly Console.WriteLine("Testing RSA Encoding methods...") Console.WriteLine("EME-PKCS1-V1_5 method") msg = Cnv.FromHex("deadbeef") 'b = Rsa.EncodeMsg(64, msg, Rsa.EncodeFor.Encryption); b = Rsa.EncodeMsgForEncryption(64, msg, Rsa.EME.PKCSv1_5) Console.WriteLine("MSG={0}", Cnv.ToHex(msg)) Console.WriteLine("EME={0}", Cnv.ToHex(b)) 'bcheck = Rsa.DecodeMsg(b, Rsa.EncodeFor.Encryption); bcheck = Rsa.DecodeMsgForEncryption(b, Rsa.EME.PKCSv1_5) Console.WriteLine("MSG={0}", Cnv.ToHex(bcheck)) Debug.Assert(CompareByteArrays(bcheck, msg), "EME-PKCSv1_5 Decoding failed") ' Again using OAEP algorithm instead of default Console.WriteLine("EME-OAEP method") msg = Cnv.FromHex("deadbeef") 'b = Rsa.EncodeMsg(64, msg, Rsa.EncodeFor.Encryption_OAEP); b = Rsa.EncodeMsgForEncryption(64, msg, Rsa.EME.OAEP) Console.WriteLine("MSG={0}", Cnv.ToHex(msg)) Console.WriteLine("EME={0}", Cnv.ToHex(b)) 'bcheck = Rsa.DecodeMsg(b, Rsa.EncodeFor.Encryption_OAEP); bcheck = Rsa.DecodeMsgForEncryption(b, Rsa.EME.OAEP) Console.WriteLine("MSG={0}", Cnv.ToHex(bcheck)) Debug.Assert(CompareByteArrays(bcheck, msg), "EME-OAEP Decoding failed") ' Ditto for signature Console.WriteLine("EME-PKCS1-V1_5 method for signature") msg = System.Text.Encoding.[Default].GetBytes("abc") 'b = Rsa.EncodeMsg(64, msg, Rsa.EncodeFor.Signature); b = Rsa.EncodeMsgForSignature(64, msg, HashAlgorithm.Sha1) Console.WriteLine("MSG={0}", Cnv.ToHex(msg)) Console.WriteLine("EMSA={0}", Cnv.ToHex(b)) 'bcheck = Rsa.DecodeMsg(b, Rsa.EncodeFor.Signature); ' Note we can only ever extract the *digest* out of an EMSA block, not the original message bcheck = Rsa.DecodeDigestForSignature(b) Console.WriteLine("MD={0}", Cnv.ToHex(bcheck)) ' We expect the value of SHA-1("abc") Debug.Assert(CompareByteArrays(bcheck, Cnv.FromHex("A9993E364706816ABA3E25717850C26C9CD0D89D"))) ' Check our valid keys n = Rsa.CheckKey(sbPrivateKey.ToString()) Console.WriteLine("Rsa.CheckKey returns {0} for private key (expecting 0)", n) n = Rsa.CheckKey(sbPublicKey.ToString()) Console.WriteLine("Rsa.CheckKey returns {0} for public key (expecting 1)", n) ' and try something non-valid Console.WriteLine("Try an invalid key string...") n = Rsa.CheckKey("Some garbage in a string") Console.WriteLine("Rsa.CheckKey returns {0} (expecting -ve error code) {1};{2}", n, General.ErrorLookup(n), General.LastError()) ' Export key to XML format s = Rsa.ToXMLString(sbPrivateKey.ToString(), Rsa.XmlOptions.ForceRSAKeyValue) Console.WriteLine("Key in XML format={0}", s) ' Test re-import from XML s = Rsa.FromXMLString(s, False) ' [v3.0] This test does not work anymore: ' the internal strings are always different even for the same key 'Debug.Assert(String.Compare(s, sbPrivateKey.ToString()) == 0, ' "Private key string from XML does not match original"); ' Create a PKCS-12 file containing Alice's private key ' Updated [v3.8] Default is now with "standard" encrypted cert fname = "alice.pfx" n = Pfx.MakeFile(fname, "AliceRSASignByCarl.cer", "AlicePrivRSASign.epk", "password", "Alice's Key", Pfx.Options.[Default]) Console.WriteLine("Pfx.MakeFile returns {0} (expected 0)", n) Debug.Assert(0 = n, "Failed to create PFX file") ' Verify that we saved it OK isok = Pfx.SignatureIsValid(fname, "password") Console.WriteLine("Signature in {0} is {1}", fname, (If(isok, "OK", "INVALID"))) Debug.Assert(isok, "PFX signature is invalid") ' Extract the encrypted private key from the PKCS12 file into a new PKCS-8 file fnameOutput = "alice_epk_from_pfx.bin" n = Rsa.GetPrivateKeyFromPFX(fnameOutput, fname) Console.WriteLine("Rsa.GetPrivateKeyFromPFX returns {0} (expected +ve)", n) Debug.Assert(n > 0, "Failed to extract private key from PFX file") ' Check we got the same private key as we had before sbPrivateKey = Rsa.ReadEncPrivateKey(fnameOutput, "password") strCheck = sbPrivateKey.ToString() sbPrivateKey = Rsa.ReadEncPrivateKey("AlicePrivRSASign.epk", "password") ' Use the KeyHashCode - don't try and compare internal strings directly Debug.Assert(Rsa.KeyHashCode(strCheck) = Rsa.KeyHashCode(sbPrivateKey.ToString()), "Private keys don't match") ' Save private key in unencrypted format compatible with OpenSSL fname = "AlicePriInfo_ssl.txt" n = Rsa.SavePrivateKeyInfo(fname, sbPrivateKey.ToString(), Rsa.Format.SSL) Console.WriteLine("Rsa.SavePrivateKeyInfo returns {0} (expected 0)", n) Debug.Assert(0 = n, "Failed to create unencrypted private key file") ' Check we can read it and that it's the same as before strCheck = sbPrivateKey.ToString() s = Rsa.ReadPrivateKeyInfo(fname).ToString() ' Use the HashCode of the internal strings Console.WriteLine("Rsa.KeyHashCode(old)={0,8:X}", Rsa.KeyHashCode(strCheck)) Console.WriteLine("Rsa.KeyHashCode(new)={0,8:X}", Rsa.KeyHashCode(s)) Debug.Assert(Rsa.KeyHashCode(strCheck) = Rsa.KeyHashCode(s), "Private keys don't match") ' Read the private key directly from the PFX file [new in v3.8] fname = "alice.pfx" sbPrivateKey = Rsa.ReadPrivateKeyFromPFX(fname, "password") Console.WriteLine("Rsa.ReadPrivateKeyFromPFX returns a string {0} characters long", sbPrivateKey.ToString().Length) Debug.Assert(sbPrivateKey.ToString().Length > 0, "Rsa.ReadPrivateKeyFromPFX failed") Console.WriteLine("Rsa.KeyHashCode={0,8:X}", Rsa.KeyHashCode(sbPrivateKey.ToString())) ' Convert this to a public key string [new in v3.8] sbPublicKey = Rsa.PublicKeyFromPrivate(sbPrivateKey) Console.WriteLine("Rsa.PublicKeyFromPrivate returns a string {0} characters long", sbPublicKey.ToString().Length) Debug.Assert(sbPublicKey.ToString().Length > 0, "Rsa.PublicKeyFromPrivate failed") Console.WriteLine("Rsa.KeyHashCode={0,8:X}", Rsa.KeyHashCode(sbPublicKey.ToString())) Debug.Assert(Rsa.KeyHashCode(sbPublicKey.ToString()) = Rsa.KeyHashCode(sbPrivateKey.ToString()), "Public and private keys don't match") ' Now save the public key in PEM format fname = "pubkey_check.txt" n = Rsa.SavePublicKey(fname, sbPublicKey.ToString(), Rsa.Format.PEM) Console.WriteLine("Rsa.SavePublicKey returns {0} (expected 0)", n) Debug.Assert(0 = n, "Failed to create public key file") ' And check we can read it and it is the same strCheck = sbPublicKey.ToString() s = Rsa.ReadPublicKey(fname).ToString() ' Use the HashCode of the internal strings Console.WriteLine("Rsa.KeyHashCode(old)={0,8:X}", Rsa.KeyHashCode(strCheck)) Console.WriteLine("Rsa.KeyHashCode(new)={0,8:X}", Rsa.KeyHashCode(s)) Debug.Assert(Rsa.KeyHashCode(strCheck) = Rsa.KeyHashCode(s), "Public keys don't match") ' NOTE: [v3.0] This comparison of internal strings will no longer work. ' Debug.Assert(strCheck == s, "Public keys don't match"); ' Create a PKCS-12 file excluding Alice's private key; i.e. just the cert fname = "alice-nokey.pfx" n = Pfx.MakeFile(fname, "AliceRSASignByCarl.cer", "", "", "Alice's Cert", Pfx.Options.PlainCert) Console.WriteLine("Pfx.MakeFile (no key) returns {0} (expected 0)", n) Debug.Assert(0 = n, "Failed to create PFX file (no key)") ' Extract the certificate from a PKCS-12 PFX file fnameInput = "alice.pfx" fnameCert = "alice_fromPFX.cer" n = X509.GetCertFromPFX(fnameCert, fnameInput, "password") Console.WriteLine("X509.GetCertFromPFX returns {0} (expected +ve).", n) Debug.Assert(n > 0, "Failed to extract certificate from PFX file") ' Check it is an X.509 cert by getting its subject s = X509.CertSubjectName(fnameCert, "") Console.WriteLine("{0} has subject {1}.", fnameCert, s) ' [New v3.1] Show that a private and public key string are matched ' Read in the private key from the encrypted key file sbPrivateKey = Rsa.ReadEncPrivateKey("AlicePrivRSASign.epk", "password") Debug.Assert(sbPrivateKey.Length > 0, "Failed to read in private key") ' Read the public key from the recipient's X.509 certificate sbPublicKey = Rsa.GetPublicKeyFromCert("AliceRSASignByCarl.cer") Debug.Assert(sbPublicKey.Length > 0, "Failed to read in public key") n = Rsa.KeyMatch(sbPrivateKey.ToString(), sbPublicKey.ToString()) Console.WriteLine("Rsa.KeyMatch(PRIV_alice,PUB_alice) returned {0} (expected 0)", n) Debug.Assert(n = 0, "Rsa.KeyMatch failed") ' Try a pair that are not matched: use Alice's private key and Bob's public sbPublicKey = Rsa.GetPublicKeyFromCert("BobRSASignByCarl.cer") n = Rsa.KeyMatch(sbPrivateKey.ToString(), sbPublicKey.ToString()) Console.WriteLine("Rsa.KeyMatch(PRIV_alice,PUB_bob) returned {0} [{1}] (expected -ve)", n, General.ErrorLookup(n)) Debug.Assert(n <> 0, "Rsa.KeyMatch failed") ' Generate a fresh RSA key which we'll use later pubkeyFile = "mykey_pub.bin" prikeyFile = "mykey_epk.bin" n = Rsa.MakeKeys(pubkeyFile, prikeyFile, 512, Rsa.PublicExponent.Exp_EQ_65537, 1024, "password", _ Rsa.PbeOptions.[Default], True) Console.WriteLine("Rsa.MakeKeys returned {0}", n) Debug.Assert(n = 0, "Failed to create RSA key pair") Console.WriteLine("Created public/private key pair OK") '************* ' X509 TESTS * '************* Console.WriteLine("X509 TESTS:") ' create a new self-signed certificate (Issue No 1) using keys we just created fnameCert = "myCAcert.cer" n = X509.MakeCertSelf(fnameCert, prikeyFile, 1, 5, "CN=Me;C=AU", "myemail@here.com", _ X509.KeyUsageOptions.DigitalSignature Or X509.KeyUsageOptions.KeyCertSign, "password", X509.Options.FormatPem) Console.WriteLine("X509.MakeCertSelf returned {0}", n) ' create a new certificate for me (Issue No 101) as issued by Carl fnameCert = "mycert.cer" issuerCert = "CarlRSASelf.cer" prikeyFile = "CarlPrivRSASign.epk" n = X509.MakeCert(fnameCert, issuerCert, pubkeyFile, prikeyFile, 101, 2, _ "CN=Me;C=US;O=MyOrg", "", 0, "password", X509.Options.SigAlg_Md5WithRSAEncryption Or X509.Options.VersionOne) Console.WriteLine("X509.MakeCert returned {0}", n) ' Verify our two new certificates n = X509.VerifyCert(fnameCert, issuerCert) If 0 = n Then Console.WriteLine("OK, {0} was issued by {1}.", fnameCert, issuerCert) Else Console.WriteLine("ERROR: {0} was NOT issued by {1}.", fnameCert, issuerCert) End If fnameCert = "myCAcert.cer" issuerCert = "myCAcert.cer" n = X509.VerifyCert(fnameCert, issuerCert) If 0 = n Then Console.WriteLine("OK, {0} was issued by {1}.", fnameCert, issuerCert) Else Console.WriteLine("ERROR: {0} was NOT issued by {1}.", fnameCert, issuerCert) End If ' Get subject name from cert s = X509.CertSubjectName(fnameCert, "") Console.WriteLine("{0} has subject {1}.", fnameCert, s) ' and again fnameCert = "mycert.cer" s = X509.CertSubjectName(fnameCert, "|") Console.WriteLine("{0} has subject {1}.", fnameCert, s) ' Get SHA-1 "thumbprint" (i.e. hash digest) of cert file s = X509.CertThumb(fnameCert, HashAlgorithm.Sha1) Console.WriteLine("{0} has SHA-1 Thumbprint {1}" & vbLf & vbTab & "--Go on, use CERTMGR and check!", fnameCert, s) ' Verify a known certificate is still valid fnameCert = "CarlRSASelf.cer" Console.WriteLine("For certificate '{0}':", fnameCert) s = X509.CertIssuedOn(fnameCert) Console.WriteLine("Issued at {0}", s) s = X509.CertExpiresOn(fnameCert) Console.WriteLine("Expires at {0}", s) isok = X509.CertIsValidNow(fnameCert) If isok Then Console.WriteLine("OK, {0} is valid now.", fnameCert) Else Console.WriteLine("ERROR: {0} is NOT valid now.", fnameCert) End If ' Extract details from a certificate fnameCert = "CarlRSASelf.cer" s = X509.CertIssuerName(fnameCert, ";") Console.WriteLine("Issuer Name: {0}", s) s = X509.CertSerialNumber(fnameCert) Console.WriteLine("Serial Number: {0}", s) s = X509.HashIssuerAndSN(fnameCert, HashAlgorithm.Sha1) Console.WriteLine("Hash(IssuerName+SerialNumber) = {0}", s) ' Query the certificate for various details fnameCert = "CarlRSASelf.cer" Console.WriteLine("For X.509 certificate '{0}'", fnameCert) query = "version" s = X509.QueryCert(fnameCert, query) Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s) query = "signatureAlgorithm" s = X509.QueryCert(fnameCert, query) Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s) query = "sigAlgID" s = X509.QueryCert(fnameCert, query) Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s) query = "notBefore" s = X509.QueryCert(fnameCert, query) Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s) query = "notAfter" s = X509.QueryCert(fnameCert, query) Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s) query = "issuerName" s = X509.QueryCert(fnameCert, query) Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s) query = "subjectName" s = X509.QueryCert(fnameCert, query) Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s) query = "subjectPublicKeyAlgorithm" s = X509.QueryCert(fnameCert, query) Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s) query = "isCA" s = X509.QueryCert(fnameCert, query) Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s) ' Read in certificate data from a file into a base64-encoded string fnameCert = "CarlRSASelf.cer" certBase64 = X509.ReadStringFromFile(fnameCert) Console.WriteLine("X.509 certificate '{0}' as a string=" & vbLf & "{1}", fnameCert, certBase64) Debug.Assert(certBase64.Length > 0, "X509.ReadStringFromFile failed") ' Write back string as a new PEM-format certificate file fnameCheck = fnameCert & ".copy.pem.cer" n = X509.SaveFileFromString(fnameCheck, certBase64, True) Console.WriteLine("X509.SaveFileFromString('{0}') returns {1} (expecting 0)", fnameCheck, n) ' Read in string from new file as a check strCheck = X509.ReadStringFromFile(fnameCheck) If strCheck = certBase64 Then Console.WriteLine("OK, strings from new and old X.509 files are identical.") Else Console.WriteLine("ERROR: Strings from new and old X.509 files do not match") End If Debug.Assert(strCheck = certBase64, "X509.SaveFileFromString failed") ' And check the "thumbprints" of the two certificates s = X509.CertThumb(fnameCert, HashAlgorithm.Sha1) strCheck = X509.CertThumb(fnameCheck, HashAlgorithm.Sha1) Console.WriteLine("Thumbprint('{0}')={1}", fnameCert, s) Console.WriteLine("Thumbprint('{0}')={1}", fnameCheck, strCheck) Debug.Assert(strCheck = s, "X509.CertThumb results failed") ' [New v3.1] Use base64 string instead of certificate filename in X.509 fn s = X509.CertThumb(certBase64, HashAlgorithm.Sha1) Console.WriteLine("Thumbprint('{0}...{1}')={2}", certBase64.Substring(0, 5), certBase64.Substring(certBase64.Length - 5, 5), s) ' Make a certificate signing request using our new private key prikeyFile = "mykey_epk.bin" n = X509.CertRequest("myreq.txt", prikeyFile, "CN=myuser;O=Test Org;C=AU;L=Sydney;S=NSW", "password", 0) Console.WriteLine("X509.CertRequest returned {0} (expected 0).", n) ' Extract the certificates from a PKCS-7 cert chain file fnameInput = "bob.p7b" ' find the number of certs in the chain n = X509.GetCertFromP7Chain("", fnameInput, 0) Console.WriteLine("X509.GetCertFromP7Chain(0) returns {0} (expected 2).", n) ' extract the certs in turn For i = 1 To n fnameCert = "certfile" & i & ".cer" r = X509.GetCertFromP7Chain(fnameCert, fnameInput, i) Debug.Assert(r > 0) Console.WriteLine("Extracted certificate '{0}' ({1} bytes)", fnameCert, r) ' check its subject name s = X509.CertSubjectName(fnameCert, "") Console.WriteLine("{0} has subject {1}.", fnameCert, s) Next ' [New v3.1] Bob creates a self-signed cert using his Chinese nickname, Ben fnameCert = "benChina.cer" prikeyFile = "BobPrivRSAEncrypt.epk" ' Set name using UTF-8-encoded chinese characters: ' CN=ben (U+672C) ' C= zhong guo (U+4E2D, U+570B) ' OU=zong ju (U+7E3D, U+5C40) distname = "CN=#xe69cac;C=#xe4b8ade59c8b;OU=#xe7b8bde5b180" n = X509.MakeCertSelf(fnameCert, prikeyFile, &H888, 8, distname, "ben@ho.com.cn", _ X509.KeyUsageOptions.DigitalSignature Or X509.KeyUsageOptions.KeyCertSign, "password", X509.Options.UTF8String) Console.WriteLine("X509.MakeCertSelf(chinese) returned {0}", n) Debug.Assert(n = 0, "Failed to create new certificate") ' Read in the distinguished name from our new certificate s = X509.CertSubjectName(fnameCert, "") Console.WriteLine("Subject name is '{0}'", s) ' Ben then creates a certificate for a Mexican user '(using the public key we created above and his chinese certificate!) issuerCert = "benChina.cer" fnameCert = "mariaMexico.cer" ' passing the UTF-8 characters for the name in [v3.1] hex format ' C=México;CN=María;OU=#xabc (yes, we want the OU to be "#xabc") distname = "C=#x4de97869636f;CN=#x4d6172ed61;OU=##xabc" n = X509.MakeCert(fnameCert, issuerCert, pubkeyFile, prikeyFile, 7, 2, _ distname, "", 0, "password", X509.Options.UTF8String) Console.WriteLine("X509.MakeCertSelf(mexican) returned {0}", n) Debug.Assert(n = 0, "Failed to create new certificate") ' Now read in the Subject's name using QueryCert ' forcing the name to be converted back to Latin-1 encoding s = X509.QueryCert(fnameCert, "subjectName", X509.Options.Latin1) Console.WriteLine("Subject's name is '{0}'", s) Debug.Assert(s.Length > 0, "Failed to read subjectName") ' [new in v3.3] Make an end-user cert identical to RFC4134 AliceRSASignByCarl.cer ' First we need to extract the public key from the cert (as though we were startng with it) pubkeyFile = "AlicePubRSA.pub" sbPublicKey = Rsa.GetPublicKeyFromCert("AliceRSASignByCarl.cer") Debug.Assert(sbPublicKey.Length > 0, "Failed to get public key from cert") r = Rsa.SavePublicKey(pubkeyFile, sbPublicKey.ToString(), Rsa.Format.[Default]) ' Uses Alice's public key; signed by Carl issuerCert = "CarlRSASelf.cer" prikeyFile = "CarlPrivRSASign.epk" password = "password" dn = "CN=AliceRSA" extns = "rfc822name=AliceRSA@example.com;" & "serialNumber=46346BC7800056BC11D36E2EC410B3B0;" & "subjectKeyIdentifier=77D2B4D1B74C8A8AA3CE459DCEEC3CA03AE3FF50;" & "notBefore=1999-09-19T01:08:47;" & "notAfter=2039-12-31;" Dim kuo As X509.KeyUsageOptions = X509.KeyUsageOptions.DigitalSignature Or X509.KeyUsageOptions.NonRepudiation fnameCert = "AliceRSA-dup.cer" r = X509.MakeCert(fnameCert, issuerCert, pubkeyFile, prikeyFile, 0, 0, _ dn, extns, kuo, password, X509.Options.AuthKeyId) Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r) Debug.Assert(r = 0, "X509.MakeCert failed") Console.WriteLine("Created cert file {0}", fnameCert) ' Now check we got the same as our original hexDigest = X509.CertThumb(fnameCert, HashAlgorithm.Sha1) Console.WriteLine("{1}=SHA-1({0})", fnameCert, hexDigest) fnameCheck = "AliceRSASignByCarl.cer" strCheck = X509.CertThumb(fnameCheck, HashAlgorithm.Sha1) Console.WriteLine("{1}=SHA-1({0})", fnameCheck, strCheck) Debug.Assert(hexDigest = strCheck, "Digests are not equal") ' [new in v3.5] Create a new certificate using a PKCS-10 certificate signing request (CSR) ' instead of using a subjectPublicKey file and distininguished name. Console.WriteLine("Create a new certificate using a PKCS-10 CSR file...") csrFile = "myreq.txt" X509.TextDump("dump.txt", csrFile) PrintATextFile("dump.txt") fnameCert = "mycert-fromCSR.cer" issuerCert = "CarlRSASelf.cer" prikeyFile = "CarlPrivRSASign.epk" password = "password" extns = "notBefore=2010-01-01T12:00" ' Make an end-user cert valid for 4 years from 2010-01-01 using PKCS#10 CSR file. ' Note that we pass the name of the CSR file instead of the subjectPublicKey file ' and pass the empty string for distName to flag that we have a CSR file. r = X509.MakeCert(fnameCert, issuerCert, csrFile, prikeyFile, &H109, 4, _ "", extns, 0, password, 0) Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r) Debug.Assert(r = 0, "X509.MakeCert failed") Console.WriteLine("Created cert file {0}", fnameCert) X509.TextDump("dump.txt", fnameCert) PrintATextFile("dump.txt") ' [new in v3.5] Validate certificate paths either in a P7 cert chain file or in a list of certs ' with and without a trusted certificate fname = "bob.p7b" Console.WriteLine("Validate certificate path in file {0}...", fname) r = X509.ValidatePath(fname) Console.WriteLine("X509.ValidatePath returns {0} (expecting 0)", r) Debug.Assert(r = 0, "X509.ValidatePath failed") certList = "BobRSASignByCarl.cer;CarlRSASelf.cer" certFile = "CarlRSASelf.cer" Console.WriteLine("Validate certificate path in list '{0}' using trusted cert {1}...", certList, certFile) r = X509.ValidatePath(certList, certFile, False) Console.WriteLine("X509.ValidatePath returns {0} (expecting 0)", r) Debug.Assert(r = 0, "X509.ValidatePath failed") '****************************************** ' CRL (CERTIFICATE REVOCATION LIST) TESTS * '****************************************** ' [new in v3.5] Console.WriteLine("CRL (CERTIFICATE REVOCATION LIST) TESTS:") ' Carl creates a Certificate Revocation List (CRL) ' -- A CRL dated with the current system time crlFile = "CarlsNew.crl" issuerCert = "CarlRSASelf.cer" prikeyFile = "CarlPrivRSASign.epk" password = "password" certList = "1,2007-12-31; 2, 2009-12-31T12:59:59Z; 66000,2066-01-01; #x0102deadbeef,2010-02-28T01:01:59" r = X509.MakeCRL(crlFile, issuerCert, prikeyFile, password, certList, "", _ 0) Console.WriteLine("X509.MakeCRL returns {0} (expecting 0)", r) Debug.Assert(r = 0, "X509.MakeCRL failed") Console.WriteLine("Created CRL file {0}", crlFile) ' -- A CRL using specified times (NB these are GMT times, not local) ' -- with an empty revocation list and using sha256WithRSAEncryption to sign crlFile = "Carl_20100401.crl" extns = "thisUpdate=2010-04-01T12:00;nextUpdate=2010-05-01" r = X509.MakeCRL(crlFile, issuerCert, prikeyFile, password, "", extns, _ X509.Options.SigAlg_Sha256WithRSAEncryption) Console.WriteLine("X509.MakeCRL returns {0} (expecting 0)", r) Debug.Assert(r = 0, "X509.MakeCRL failed") Console.WriteLine("Created CRL file {0}", crlFile) ' Show we can now use VerifyCert to check the signature in a CRL r = X509.VerifyCert(crlFile, issuerCert) Console.WriteLine("X509.VerifyCert returns {0} (expecting 0)", r) Debug.Assert(r = 0, "X509.VerifyCert failed") Console.WriteLine("OK, CRL file {0} was signed by owner of {1}", crlFile, issuerCert) ' Check if a given cert is in a CRL ' Use test CRL and certs from RFC3280 crlFile = "rfc3280bis_CRL.crl" ' This cert has not been revoked -- expected result = zero certFile = "rfc3280bis_cert1.cer" Console.WriteLine("CRL ={0}", crlFile) Console.WriteLine("Cert={0}", certFile) r = X509.CheckCertInCRL(certFile, crlFile, "", "") Console.WriteLine("X509.CheckCertInCRL returns {0} (expecting 0)", r) Debug.Assert(r = 0, "X509.CheckCertInCRL failed") If X509.Revoked = r Then Console.WriteLine("CERT HAS BEEN REVOKED") ElseIf 0 = r Then Console.WriteLine("Cert has not been revoked") Else Console.WriteLine("ERROR: {0}: {1}", General.ErrorLookup(r), General.LastError()) End If ' This cert HAS been revoked -- expected result = +1 certFile = "rfc3280bis_cert2.cer" Console.WriteLine("CRL ={0}", crlFile) Console.WriteLine("Cert={0}", certFile) r = X509.CheckCertInCRL(certFile, crlFile, "", "") Console.WriteLine("X509.CheckCertInCRL returns {0} (expecting 0)", r) Debug.Assert(r = 1, "X509.CheckCertInCRL failed") If X509.Revoked = r Then Console.WriteLine("CERT HAS BEEN REVOKED") ElseIf 0 = r Then Console.WriteLine("Cert has not been revoked") Else Console.WriteLine("ERROR: {0}: {1}", General.ErrorLookup(r), General.LastError()) End If ' But the same cert was not revoked as at 15:00 GMT on 19 November 2004 -- expected result = 0 certFile = "rfc3280bis_cert2.cer" dateStr = "2004-11-19T15:00Z" Console.WriteLine("CRL ={0}", crlFile) Console.WriteLine("Cert={0}", certFile) Console.WriteLine("Date={0}", dateStr) r = X509.CheckCertInCRL(certFile, crlFile, "", dateStr) Console.WriteLine("X509.CheckCertInCRL returns {0} (expecting 0)", r) Debug.Assert(r = 0, "X509.CheckCertInCRL failed") If X509.Revoked = r Then Console.WriteLine("CERT HAS BEEN REVOKED") ElseIf 0 = r Then Console.WriteLine("Cert has not been revoked") Else Console.WriteLine("ERROR: {0}: {1}", General.ErrorLookup(r), General.LastError()) End If '************************************************** ' OCSP (ONLINE CERTIFICATE STATUS PROTOCOL) TESTS * '************************************************** ' [new in v3.5] Console.WriteLine("OCSP (ONLINE CERTIFICATE STATUS PROTOCOL) TESTS:") ' Creates an OCSP request to check our own current code signing certificate file dims.cer. ' This was issued by the holder of certificate in the file UTNUSERFirst-Object.cer. issuerCert = "UTNUSERFirst-Object.cer" certFile = "dims.cer" Console.WriteLine("IssuerFile={0}", issuerCert) Console.WriteLine("CertFile={0}", certFile) s = Ocsp.MakeRequest(issuerCert, certFile, 0) Debug.Assert(s.Length > 0, "Ocsp.MakeRequest failed") Console.WriteLine("OCSPRequest={0}", s) ' Pass a hex serial number instead of filename snStr = "#x 00 FB C7 23 22 8C 8C 80 22 D8 85 92 23 DE E7 06 60" Console.WriteLine("Cert SerialNumber={0}", snStr) s = Ocsp.MakeRequest(issuerCert, snStr, 0) Debug.Assert(s.Length > 0, "Ocsp.MakeRequest failed") Console.WriteLine("OCSPRequest={0}", s) ' We use a response received from ocsp.usertrust.com for our own code signing certificate ' (this HAS been signed by our cert's issuer) fname = "ocsp_response_ok_dims.dat" issuerCert = "UTNUSERFirst-Object.cer" Console.WriteLine("ResponseFile={0}", fname) Console.WriteLine("IssuerFile={0}", issuerCert) s = Ocsp.ReadResponse(fname, issuerCert) Debug.Assert(s.Length > 0, "Ocsp.ReadResponse failed") Console.WriteLine("OCSPResponse={0}", s) '***************************** ' CMS (S/MIME OBJECTS) TESTS * '***************************** Console.WriteLine("CMS (S/MIME OBJECTS) TESTS:") ' Create a test text file fnameInput = "excontent.txt" MakeATextFile(fnameInput, "This is some sample content.") ' Create an enveloped CMS object from Alice to Bob using Bob's X.509 certificate fnameOutput = "cmsalice2bob.p7m" fnameCert = "BobRSASignByCarl.cer" ' This should return 1 (indicating one successful recipient) n = Cms.MakeEnvData(fnameOutput, fnameInput, fnameCert, 0) Console.WriteLine("Cms.MakeEnvData returns {0} (expecting 1)", n) Debug.Assert(1 = n, "Cms.MakeEnvData failed") ' Now try and read it using Bob's private key fnameOutput = "cmsalice2bob.p7m.txt" fnameInput = "cmsalice2bob.p7m" sbPrivateKey = Rsa.ReadEncPrivateKey("BobPrivRSAEncrypt.epk", "password") Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key") n = Cms.ReadEnvDataToFile(fnameOutput, fnameInput, "", sbPrivateKey.ToString(), 0) Console.WriteLine("Cms.ReadEnvData returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.ReadEnvData failed") s = ReadATextFile(fnameOutput) Console.WriteLine("MSG={0}", s) ' and again but read directly into a string s = Cms.ReadEnvDataToString(fnameInput, "", sbPrivateKey.ToString(), 0) Console.WriteLine("MSG={0}", s) Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed") ' Try the BigFile option [new in v3.7] If doBigFile Then fname = "bigfile.txt" flen = MakeALargeTextFile(fname) Console.WriteLine("Created *big* file '{0}' of length {1} bytes", fname, flen) ' Create an enveloped CMS object from Alice to Bob using Bob's X.509 certificate fnameInput = fname fnameOutput = "cmsalice2bob-big.p7m" fnameCert = "BobRSASignByCarl.cer" ' Call MakeEnvData using BigFile option (may be a noticable delay here) Console.WriteLine("About to envelope using BigFile option...") n = Cms.MakeEnvData(fnameOutput, fnameInput, fnameCert, Cms.Options.BigFile) Console.WriteLine("Cms.MakeEnvData returns {0} (expecting 1)", n) Debug.Assert(1 = n, "Cms.MakeEnvData failed") ' Now try and decrypt using Bob's private key (which we already have from above) fnameInput = fnameOutput fnameOutput = fnameInput & ".chk.txt" ' Use BigFile option Console.WriteLine("About to decrypt using BigFile option...") n = Cms.ReadEnvDataToFile(fnameOutput, fnameInput, "", sbPrivateKey.ToString(), Cms.Options.BigFile) Console.WriteLine("Cms.ReadEnvData returns {0} (expecting 0)", n) Console.WriteLine("Decrypted file '{0}' is {1} bytes long.", fnameOutput, FileLength(fnameOutput)) Debug.Assert(0 = n, "Cms.ReadEnvData failed") End If ' Generate a BER-encoded CMS signedData object as a file ' using Alice's private key sbPrivateKey = Rsa.ReadEncPrivateKey("AlicePrivRSASign.epk", "password") fnameOutput = "BasicSignByAlice.bin" fnameInput = "excontent.txt" fnameCert = "AliceRSASignByCarl.cer" n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(), 0) Console.WriteLine("Cms.MakeSigData returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.MakeSigData failed") ' again using a string as input instead of a file s = ReadATextFile(fnameInput) fname = "BasicSignByAlice1.bin" n = Cms.MakeSigDataFromString(fname, s, fnameCert, sbPrivateKey.ToString(), 0) Console.WriteLine("Cms.MakeSigDataFromString returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.MakeSigDataFromString failed") ' Check we got the same result by comparing the files b = ReadABinaryFile(fnameOutput) bcheck = ReadABinaryFile(fname) Debug.Assert(CompareByteArrays(b, bcheck) = True, "SigData files are not identical") ' Make a detached signature using the message digest hash fnameOutput = "DetSignByAlice.bin" hexDigest = "406aec085279ba6e16022d9e0629c0229687dd48" n = Cms.MakeDetachedSig(fnameOutput, hexDigest, fnameCert, sbPrivateKey.ToString(), 0) Console.WriteLine("Cms.MakeDetachedSig returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.MakeDetachedSig failed") ' Extract the contents from the signed object into another file fnameOutput = "excontent_chk.txt" fnameInput = "BasicSignByAlice.bin" n = Cms.ReadSigDataToFile(fnameOutput, fnameInput) ' returns value is size of file or -ve error code Console.WriteLine("Cms.ReadSigDataToFile returns {0} (expecting 28)", n) Debug.Assert(n >= 0, "Cms.ReadSigDataToFile failed") strCheck = ReadATextFile(fnameOutput) Console.WriteLine("MSG(file)={0}", strCheck) ' Extract the contents directly into a string instead s = Cms.ReadSigDataToString(fnameInput) Console.WriteLine("MSG(string)={0}", s) Debug.Assert([String].Compare(s, strCheck) = 0, "Contents are different") ' Make a signed object in base64 format fnameOutput = "BasicSignByAlice_64.txt" fnameInput = "excontent.txt" fnameCert = "AliceRSASignByCarl.cer" n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(), Cms.Options.FormatBase64) Console.WriteLine("Cms.MakeSigData returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.MakeSigData failed") ' and read back into a string fnameInput = fnameOutput s = Cms.ReadSigDataToString(fnameInput) Console.WriteLine("MSG(string)={0}", s) Debug.Assert([String].Compare(s, strCheck) = 0, "Contents are different") ' Make a signed object with signingTime attribute fnameOutput = "BasicSignByAlice_attr.bin" fnameInput = "excontent.txt" fnameCert = "AliceRSASignByCarl.cer" n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(), Cms.Options.IncludeAttributes Or Cms.Options.AddSignTime) Console.WriteLine("Cms.MakeSigData returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.MakeSigData failed") ' Extract and verify the signed hash digest from the signed-data object fnameInput = "BasicSignByAlice.bin" fnameCert = "AliceRSASignByCarl.cer" s = Cms.GetSigDataDigest(fnameInput, fnameCert) Console.WriteLine("DIG={0}", s) Debug.Assert([String].Compare(s, hexDigest) = 0, "Hash digests are different") ' ditto from a file in base64 format but with no signature verification fnameInput = "BasicSignByAlice_64.txt" s = Cms.GetSigDataDigest(fnameInput, "") Console.WriteLine("DIG={0}", s) Debug.Assert([String].Compare(s, hexDigest) = 0, "Hash digests are different") ' Verify the signature directly fnameInput = "BasicSignByAlice.bin" n = Cms.VerifySigData(fnameInput) Console.WriteLine("Cms.VerifySigData returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.VerifySigData failed") ' Get the hash algorithm ID of the signature fnameInput = "BasicSignByAlice.bin" n = Cms.GetSigHashAlgorithm(fnameInput, fnameCert) Console.WriteLine("Cms.GetSigHashAlgorithm returns {0} (expecting 0 => SHA-1)", n) Debug.Assert(0 = n, "Cms.GetSigHashAlgorithm failed") ' Try using the wrong certificate fnameInput = "BasicSignByAlice.bin" fnameCert = "BobRSASignByCarl.cer" n = Cms.GetSigHashAlgorithm(fnameInput, fnameCert) Console.WriteLine("Cms.GetSigHashAlgorithm returns {0} (expecting -ve error code)", n) Console.WriteLine("{0};{1}", General.ErrorLookup(n), General.LastError()) Debug.Assert(n < 0, "Cms.GetSigHashAlgorithm succeeded when should have failed!") ' Try using a file that's not a valid signed-data object, e.g. a cert fnameInput = "CarlRSASelf.cer" fnameCert = "AliceRSASignByCarl.cer" n = Cms.GetSigHashAlgorithm(fnameInput, fnameCert) Console.WriteLine("Cms.GetSigHashAlgorithm returns {0} (expecting -ve error code)", n) Console.WriteLine("{0};{1}", General.ErrorLookup(n), General.LastError()) Debug.Assert(n < 0, "Cms.GetSigHashAlgorithm succeeded when should have failed!") ' Make a signed-data object file using MD5 and in base64 format ' [v3.7] new option for HashAlgorithm fnameOutput = "BasicSignByAlice_MD5.txt" fnameInput = "excontent.txt" fnameCert = "AliceRSASignByCarl.cer" n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(), HashAlgorithm.Md5, Cms.SigDataOptions.FormatBase64) Console.WriteLine("Cms.MakeSigData returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.MakeSigData failed") ' Get the hash algorithm ID of the signature fnameInput = fnameOutput n = Cms.GetSigHashAlgorithm(fnameInput, fnameCert) Console.WriteLine("Cms.GetSigHashAlgorithm returns {0} (expecting 1 => MD5)", n) Debug.Assert(1 = n, "Cms.GetSigHashAlgorithm failed") ' Added new overload v3.7 [2011-06-27] ' Make a signed-data object file using SHA-256 fnameOutput = "BasicSignByAlice_SHA256.bin" fnameInput = "excontent.txt" fnameCert = "AliceRSASignByCarl.cer" n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(), HashAlgorithm.Sha256, 0) Console.WriteLine("Cms.MakeSigData returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.MakeSigData failed") ' Get the hash algorithm used in the signature fnameInput = fnameOutput s = Cms.QuerySigData(fnameInput, "digestAlgorithm") Console.WriteLine("digestAlgorithm={0}", s) ' Verify the signature n = Cms.VerifySigData(fnameInput, fnameCert) Console.WriteLine("Cms.VerifySigData returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.VerifySigData failed") ' Again using a string as input instead of a file ' Use SHA-224 and include signed attributes with sign-time s = ReadATextFile("excontent.txt") fname = "BasicSignByAlice_224.bin" n = Cms.MakeSigDataFromString(fname, s, fnameCert, sbPrivateKey.ToString(), HashAlgorithm.Sha224, Cms.SigDataOptions.IncludeAttributes Or Cms.SigDataOptions.AddSignTime) Console.WriteLine("Cms.MakeSigDataFromString returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.MakeSigDataFromString failed") ' Query the signature file we've just made fnameInput = fname Console.WriteLine("For file '{0}':", fnameInput) query = "digestAlgorithm" s = Cms.QuerySigData(fnameInput, query) Console.WriteLine("{0}={1}", query, s) query = "HASsignedAttributes" s = Cms.QuerySigData(fnameInput, query) Console.WriteLine("{0}={1}", query, s) query = "signingTime" s = Cms.QuerySigData(fnameInput, query) Console.WriteLine("{0}={1}", query, s) ' Verify the signature n = Cms.VerifySigData(fnameInput) Console.WriteLine("Cms.VerifySigData returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.VerifySigData failed") ' Added v3.4.1 [2010-02-23] ' Create a "certs-only" SignedData file == certificate chain file fnameOutput = "Alice_new.p7c" certList = "AliceRSASignByCarl.cer" & ";" & "CarlRSASelf.cer" 'n = Cms.MakeSigData(fnameOutput, "", certList, "", (Cms.Options)0x0400); n = Cms.MakeSigData(fnameOutput, "", certList, "", Cms.Options.CertsOnly) Console.WriteLine("Cms.MakeSigData(certs-only) returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Cms.MakeSigData failed") ' Query our SignedData files fnameInput = "BasicSignByAlice_attr.bin" query = "version" s = Cms.QuerySigData(fnameInput, query) Console.WriteLine("Cms.QuerySigData('{0}') returns '{1}'", query, s) Debug.Assert(s.Length > 0, "QuerySigData failed") query = "digestAlgorithm" s = Cms.QuerySigData(fnameInput, query) Console.WriteLine("Cms.QuerySigData('{0}') returns '{1}'", query, s) Debug.Assert(s.Length > 0, "QuerySigData failed") query = "signingTime" s = Cms.QuerySigData(fnameInput, query) Console.WriteLine("Cms.QuerySigData('{0}') returns '{1}'", query, s) Debug.Assert(s.Length > 0, "QuerySigData failed") ' ...that's enough CMS tests. '************* ' HASH TESTS * '************* Console.WriteLine("HASH DIGEST TESTS:") s = Hash.HexFromString("abc", HashAlgorithm.Sha1) Console.WriteLine("SHA-1('abc') ={0}", s) s = Hash.HexFromString("abc", HashAlgorithm.Md5) Console.WriteLine("MD5('abc') ={0}", s) s = Hash.HexFromString("abc", HashAlgorithm.Md2) Console.WriteLine("MD2('abc') ={0}", s) s = Hash.HexFromString("abc", HashAlgorithm.Sha256) Console.WriteLine("SHA-256('abc')=" & vbLf & "{0}", s) s = Hash.HexFromString("abc", HashAlgorithm.Sha384) Console.WriteLine("SHA-384('abc')=" & vbLf & "{0}", s) s = Hash.HexFromString("abc", HashAlgorithm.Sha512) Console.WriteLine("SHA-512('abc')=" & vbLf & "{0}", s) ' Create a test file fnameInput = "hello.txt" MakeATextFile(fnameInput, "hello world" & vbCr & vbLf) ' get digest from binary file s = Hash.HexFromFile(fnameInput, HashAlgorithm.Sha1) Console.WriteLine("SHA1('hello world+CR+LF')={0}", s) ' and again treating CR-LF as a single LF ' (we use this when moving between Unix and Windows systems) s = Hash.HexFromTextFile(fnameInput, HashAlgorithm.Sha1) Console.WriteLine("SHA1('hello world+LF')= {0}", s) '************* ' HMAC TESTS * '************* Console.WriteLine("HMAC TESTS:") ' Test case 2 from RFC 2202 and RFC 4231 ' key = "Jefe" ' key_len 4 ' data = "what do ya want for nothing?" ' data_len = 28 Console.WriteLine("Test case 2 from RFC 2202 and RFC 4231...") ' Convert strings to byte arrays arrKey = System.Text.Encoding.[Default].GetBytes("Jefe") msg = System.Text.Encoding.[Default].GetBytes("what do ya want for nothing?") ' Compute HMAC-SHA-1 then check against known test vector s = Hmac.HexFromBytes(msg, arrKey, HashAlgorithm.Sha1) Console.WriteLine("HMAC-SHA-1('WDYWFN?','Jefe')={0}", s) strCheck = "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79" Console.WriteLine("CORRECT= {0}", strCheck) Debug.Assert([String].Compare(strCheck, s, True) = 0, "HMAC does not match test vector") ' Compute HMAC-SHA-256 then check against known test vector s = Hmac.HexFromBytes(msg, arrKey, HashAlgorithm.Sha256) Console.WriteLine("HMAC-SHA-256('WDYWFN?','Jefe')=" & vbLf & "{0}", s) strCheck = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843" Console.WriteLine("CORRECT=" & vbLf & "{0}", strCheck) Debug.Assert([String].Compare(strCheck, s, True) = 0, "HMAC does not match test vector") ' Test case 4 from RFC 2202 and RFC 4231 ' key = 0x0102030405060708090a0b0c0d0e0f10111213141516171819 ' key_len 25 ' data = 0xcd repeated 50 times ' data_len = 50 Console.WriteLine("Test case 4 from RFC 2202 and RFC 4231...") arrKey = New Byte(24) {} For i = 0 To 24 arrKey(i) = CByte((i + 1)) Next Console.WriteLine("Key={0}", Cnv.ToHex(arrKey)) msg = New Byte(49) {} For i = 0 To 49 msg(i) = &HCD Next Console.WriteLine("Msg={0}", Cnv.ToHex(msg)) ' Output in byte format ' Compute HMAC-SHA-1 then check against known test vector b = Hmac.BytesFromBytes(msg, arrKey, HashAlgorithm.Sha1) Console.WriteLine("HMAC-SHA-1(50(0xcd), 0x0102..19)={0}", Cnv.ToHex(b)) strCheck = "4c9007f4026250c6bc8414f9bf50c86c2d7235da" Console.WriteLine("CORRECT= {0}", strCheck) Debug.Assert([String].Compare(strCheck, Cnv.ToHex(b), True) = 0, "HMAC does not match test vector") ' Compute HMAC-SHA-256 then check against known test vector b = Hmac.BytesFromBytes(msg, arrKey, HashAlgorithm.Sha256) Console.WriteLine("HMAC-SHA-256(50(0xcd), 0x0102..19)=" & vbLf & "{0}", Cnv.ToHex(b)) strCheck = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b" Console.WriteLine("CORRECT=" & vbLf & "{0}", strCheck) Debug.Assert([String].Compare(strCheck, Cnv.ToHex(b), True) = 0, "HMAC does not match test vector") '****************** ' WIPE DATA TESTS * '****************** Console.WriteLine("WIPE DATA TESTS:") fname = "ImportantSecret.txt" MakeATextFile(fname, "Very important secrets here.") s = ReadATextFile(fname) isok = Wipe.File(fname) Console.WriteLine("Wipe.File {0}", (If(isok, "OK", "FAILED!"))) b = System.Text.Encoding.[Default].GetBytes("Secret data") Console.WriteLine("Before Wipe.Data, b = [{0}]", System.Text.Encoding.[Default].GetString(b)) Wipe.Data(b) Console.WriteLine("After Wipe.Data, b = [{0}]", System.Text.Encoding.[Default].GetString(b)) Console.WriteLine("Before Wipe.String, sbPrivateKey contains {0} characters.", sbPrivateKey.Length) Wipe.[String](sbPrivateKey) Console.WriteLine("After Wipe.String, sbPrivateKey = [{0}]", sbPrivateKey.ToString()) '************************ ' PASSWORD PROMPT TESTS * '************************ If doPrompt Then s = Pwd.Prompt(32, "My caption for the dialog here") Console.WriteLine("Password=[{0}]", s) s = Pwd.Prompt(32, "My caption for the dialog here", "My new prompt:") Console.WriteLine("Password=[{0}]", s) End If '************ ' RNG TESTS * '************ Console.WriteLine("GENERATE SOME RANDOM DATA...") For i = 0 To 2 b = Rng.Bytes(24) Console.WriteLine("RNG={0}", Cnv.ToHex(b)) Next ' And some random numbers in a given range Console.Write("{random x : -10<=x<=+10} = ") For i = 0 To 11 n = Rng.Number(-10, +10) Console.Write("{0} ", n) Next Console.Write(vbLf) ' Initialize with a seed file fname = "seed.dat" If Not FileExists(fname) Then ' No seed file yet, so we'll make one If doPrompt Then ' prompting the user for keyboard entropy Console.WriteLine("Creating a new seed file...") isok = Rng.MakeSeedFile(fname) Console.WriteLine("Rng.MakeSeedFile('{0}') returns {1} (expecting 0)", fname, isok) Debug.Assert(True = isok, "Rng.MakeSeedFile failed") Else ' Create a dummy seed file to avoid the annoying prompt. ' (this will get overwritten with real random data when initialized) b = New Byte(Rng.SeedFileSize - 1) {} MakeABinaryFile(fname, b) End If End If isok = Rng.Initialize(fname) Console.WriteLine("Rng.Initialize('{0}') returns {1} (expecting 0)", fname, isok) Debug.Assert(True = isok, "Rng.Initialize failed") Console.WriteLine("Generate some more random data...") For i = 0 To 2 b = Rng.Bytes(24) Console.WriteLine("RNG={0}", Cnv.ToHex(b)) Next ' Update the seedfile isok = Rng.UpdateSeedFile(fname) Console.WriteLine("Rng.UpdateSeedFile('{0}') returns {1} (expecting 0)", fname, isok) Debug.Assert(True = isok, "Rng.UpdateSeedFile failed") If doPrompt Then Console.WriteLine("Ask user to type some random keystrokes to add entropy...") b = Rng.BytesWithPrompt(16) Console.WriteLine("RNG={0}", Cnv.ToHex(b)) Console.WriteLine("And again...") b = Rng.BytesWithPrompt(16, "Type until we reach 128 bits...", CryptoSysPKI.Rng.Strength.Bits_128) Console.WriteLine("RNG={0}", Cnv.ToHex(b)) End If ' Test the ability to add "user-entropy"... s = "this is some user entropy in a string, well it should be!" b = Rng.Bytes(16, s) Console.WriteLine("RNG={0}", Cnv.ToHex(b)) ' and with some bytes as a "seed"... b = New Byte(3) {&HDE, &HAD, &HBE, &HEF} Console.WriteLine("'seed'={0}", Cnv.ToHex(b)) b = Rng.Bytes(16, b) Console.WriteLine("RNG={0}", Cnv.ToHex(b)) ' Do some tests on the RNG function fname = "Fips140.txt" isok = Rng.Test(fname) Console.WriteLine("Rng.Test('{0}') returns {1} (expecting True)", fname, isok) Debug.Assert(True = isok, "Rng.Test failed") isok = Rng.Test("") Console.WriteLine("Rng.Test('{0}') returns {1} (expecting True)", "", isok) Debug.Assert(True = isok, "Rng.Test failed") '**************************************** ' GENERIC BLOCK CIPHER ENCRYPTION TESTS * '**************************************** Console.WriteLine("TESTING CIPHER(tdea, hex):") ' Encrypt in CBC mode using hex strings keyhex = "737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32" ivhex = "B36B6BFB6231084E" plainStr = "5468697320736F6D652073616D706520636F6E74656E742E0808080808080808" cipherStr = "d76fd1178fbd02f84231f5c1d2a2f74a4159482964f675248254223daf9af8e4" s = Cipher.Encrypt(plainStr, keyhex, ivhex, CipherAlgorithm.Tdea, Mode.CBC) Console.WriteLine("KY={0}", keyhex) Console.WriteLine("IV={0}", ivhex) Console.WriteLine("PT={0}", plainStr) Console.WriteLine("CT={0}", s) Console.WriteLine("OK={0}", cipherStr) Debug.Assert([String].Compare(s, cipherStr, True) = 0, "Cipher.Encrypt{Hex,tdea-CBC} failed") ' Decrypt s = Cipher.Decrypt(cipherStr, keyhex, ivhex, CipherAlgorithm.Tdea, Mode.CBC) Console.WriteLine("P'={0}", s) Console.WriteLine("OK={0}", plainStr) Debug.Assert([String].Compare(s, plainStr, True) = 0, "Cipher.Decrypt{Hex,tdea-CBC} failed") Console.WriteLine("TESTING CIPHER(aes128, hex):") ' AES using hex strings keyhex = "0123456789ABCDEFF0E1D2C3B4A59687" ivhex = "FEDCBA9876543210FEDCBA9876543210" ' "Now is the time for all good men" plainStr = "4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E" cipherStr = "c3153108a8dd340c0bcb1dfe8d25d2320ee0e66bd2bb4a313fb75c5638e9e177" s = Cipher.Encrypt(plainStr, keyhex, ivhex, CipherAlgorithm.Aes128, Mode.CBC) Console.WriteLine("KY={0}", keyhex) Console.WriteLine("IV={0}", ivhex) Console.WriteLine("PT={0}", plainStr) Console.WriteLine("CT={0}", s) Console.WriteLine("OK={0}", cipherStr) Debug.Assert([String].Compare(s, cipherStr, True) = 0, "Cipher.Encrypt{Hex,aes128-CBC} failed") ' Decrypt s = Cipher.Decrypt(cipherStr, keyhex, ivhex, CipherAlgorithm.Aes128, Mode.CBC) Console.WriteLine("P'={0}", s) Console.WriteLine("OK={0}", plainStr) Debug.Assert([String].Compare(s, plainStr, True) = 0, "Cipher.Decrypt{Hex,tdea-CBC} failed") ' Test some errors Console.WriteLine("TESTING CIPHER ERRORS:") Console.WriteLine("Test an invalid key...") s = Cipher.Encrypt(plainStr, "GARBAGEKEY", "", CipherAlgorithm.Tdea, Mode.ECB) Console.Write("Cipher.Encrypt returns '{0}' ", s) n = General.ErrorCode() Console.WriteLine("ErrorCode={0} {1}", n, General.ErrorLookup(n)) Console.WriteLine("Test an invalid IV...") s = Cipher.Encrypt(plainStr, keyhex, "DEADIV", CipherAlgorithm.Tdea, Mode.CTR) Console.Write("Cipher.Encrypt returns '{0}' ", s) n = General.ErrorCode() Console.WriteLine("ErrorCode={0} {1}", n, General.ErrorLookup(n)) Console.WriteLine("Test invalid length for plaintext...") s = Cipher.Encrypt("DEADBEEF", keyhex, ivhex, CipherAlgorithm.Tdea, Mode.ECB) Console.Write("Cipher.Encrypt returns '{0}' ", s) n = General.ErrorCode() Console.WriteLine("ErrorCode={0} {1}", n, General.ErrorLookup(n)) Console.WriteLine("...END TESTING CIPHER ERRORS.") ' ADVANCED PRIVATE KEY SAVING OPTIONS - NEW OVERLOAD for Rsa.SaveEncPrivateKey ' Read in a known private key from its encrypted key file Console.WriteLine(vbLf & "TEST RSA SAVE ENCRYPTED PRIVATE KEY OPTIONS:") Console.WriteLine("Read in a private key...") sbPrivateKey = Rsa.ReadEncPrivateKey("AlicePrivRSASign.epk", "password") Console.WriteLine("PrivateKeyBits={0}", Rsa.KeyBits(sbPrivateKey.ToString())) Console.WriteLine("PrivateKeyBytes={0}", Rsa.KeyBytes(sbPrivateKey.ToString())) Console.WriteLine("KeyHashCode={0,8:X}", Rsa.KeyHashCode(sbPrivateKey.ToString())) ' Save with some new options, then check we can read fname = "alice_rsa_aes128sha1_epk.bin" n = Rsa.SaveEncPrivateKey(fname, sbPrivateKey.ToString(), 3000, "password", CipherAlgorithm.Aes128, HashAlgorithm.Sha1, _ Rsa.Format.[Default]) Console.WriteLine("Rsa.SaveEncPrivateKey returns {0} (expected 0)", n) Debug.Assert(n = 0, "Rsa.SaveEncPrivateKey failed") Console.WriteLine("Created private key file {0}", fname) sbKeyCheck = Rsa.ReadEncPrivateKey(fname, "password") Console.WriteLine("KeyHashCode={0,8:X}", Rsa.KeyHashCode(sbKeyCheck.ToString())) Debug.Assert(Rsa.KeyHashCode(sbKeyCheck.ToString()) = Rsa.KeyHashCode(sbPrivateKey.ToString()), "KeyHashCodes do not match") fname = "alice_rsa_aes192sha256_epk.bin" n = Rsa.SaveEncPrivateKey(fname, sbPrivateKey.ToString(), 3000, "password", CipherAlgorithm.Aes192, HashAlgorithm.Sha256, _ Rsa.Format.[Default]) Console.WriteLine("Rsa.SaveEncPrivateKey returns {0} (expected 0)", n) Debug.Assert(n = 0, "Rsa.SaveEncPrivateKey failed") Console.WriteLine("Created private key file {0}", fname) sbKeyCheck = Rsa.ReadEncPrivateKey(fname, "password") Console.WriteLine("KeyHashCode={0,8:X}", Rsa.KeyHashCode(sbKeyCheck.ToString())) Debug.Assert(Rsa.KeyHashCode(sbKeyCheck.ToString()) = Rsa.KeyHashCode(sbPrivateKey.ToString()), "KeyHashCodes do not match") fname = "alice_rsa_aes256sha512_epk.pem.txt" n = Rsa.SaveEncPrivateKey(fname, sbPrivateKey.ToString(), 3000, "password", CipherAlgorithm.Aes192, HashAlgorithm.Sha256, _ Rsa.Format.PEM) Console.WriteLine("Rsa.SaveEncPrivateKey returns {0} (expected 0)", n) Debug.Assert(n = 0, "Rsa.SaveEncPrivateKey failed") Console.WriteLine("Created private key file {0}", fname) sbKeyCheck = Rsa.ReadEncPrivateKey(fname, "password") Console.WriteLine("KeyHashCode={0,8:X}", Rsa.KeyHashCode(sbKeyCheck.ToString())) Debug.Assert(Rsa.KeyHashCode(sbKeyCheck.ToString()) = Rsa.KeyHashCode(sbPrivateKey.ToString()), "KeyHashCodes do not match") Console.WriteLine(vbLf & "HASH DIGEST TEST FOR SHA-224:") ' Hash of "abc" with input in hex format s = Hash.HexFromHex("616263", HashAlgorithm.Sha224) Console.WriteLine("SHA-224('abc') ={0}", s) strCheck = "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7" Console.WriteLine("Correct ={0}", strCheck) Debug.Assert([String].Compare(s, strCheck, True) = 0, "SHA-224('abc') failed") Console.WriteLine(vbLf & "MORE HMAC TESTS:") ' Hmac of <Test Case 1> with input in hex format s = Hmac.HexFromHex("4869205468657265", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", HashAlgorithm.Sha1) Console.WriteLine("HMAC-SHA-1('Hi There', (0x0b)*20)={0}", s) strCheck = "B617318655057264E28BC0B6FB378C8EF146BE00" Console.WriteLine("Correct ={0}", strCheck) Debug.Assert([String].Compare(s, strCheck, True) = 0, "HMAC-SHA-1('Hi There', (0x0b)*20) failed") s = Hmac.HexFromHex("4869205468657265", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", HashAlgorithm.Sha224) Console.WriteLine("HMAC-SHA-224('Hi There', (0x0b)*20)=" & vbLf & "{0}", s) strCheck = "896FB1128ABBDF196832107CD49DF33F47B4B1169912BA4F53684B22" Console.WriteLine("Correct =" & vbLf & "{0}", strCheck) Debug.Assert([String].Compare(s, strCheck, True) = 0, "HMAC-SHA-224('Hi There', (0x0b)*20) failed") s = Hmac.HexFromHex("4869205468657265", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", HashAlgorithm.Sha256) Console.WriteLine("HMAC-SHA-256('Hi There', (0x0b)*20)=" & vbLf & "{0}", s) strCheck = "B0344C61D8DB38535CA8AFCEAF0BF12B881DC200C9833DA726E9376C2E32CFF7" Console.WriteLine("Correct =" & vbLf & "{0}", strCheck) Debug.Assert([String].Compare(s, strCheck, True) = 0, "HMAC-SHA-256('Hi There', (0x0b)*20) failed") Console.WriteLine("") Console.WriteLine("ADVANCED ENVELOPED-DATA OBJECTS:") ' Create an enveloped CMS object Alice to Bob using Bob's X.509 certificate fname = "cms2bob_aes128.p7m" fnameCert = "BobRSASignByCarl.cer" ' This should return 1 (indicating one successful recipient) s = "This is some sample content." n = Cms.MakeEnvDataFromString(fname, s, fnameCert, CipherAlgorithm.Aes128, Cms.KeyEncrAlgorithm.Rsa_Pkcs1v1_5, 0, _ 0) Console.WriteLine("Cms.MakeEnvDataFromString returns {0} (expecting 1)", n) Debug.Assert(1 = n, "Cms.MakeEnvDataFromString failed") ' Query the enveloped-data file query = "keyEncryptionAlgorithm" s = Cms.QueryEnvData(fname, query, False) Console.WriteLine("{0}='{1}'", query, s) query = "contentEncryptionAlgorithm" s = Cms.QueryEnvData(fname, query, False) Console.WriteLine("{0}='{1}'", query, s) ' Now try and read it using Bob's private key sbPrivateKey = Rsa.ReadEncPrivateKey("BobPrivRSAEncrypt.epk", "password") Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key") s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString(), 0) Console.WriteLine("MSG={0}", s) Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed") ' [RSA-KEM withdrawn in v3.4] ' * -----BEGIN WITHDRAWN CODE----- ' // Repeat using RSA-KEM with AES-192 and SHA-256 with output in base 64 ' fname = "cms2bob_kem_a196s256.p7m.txt"; ' fnameCert = "BobRSASignByCarl.cer"; ' // This should return 1 (indicating one successful recipient) ' s = "This is some sample content."; ' n = Cms.MakeEnvDataFromString(fname, s, fnameCert, ' CipherAlgorithm.Aes192, Cms.KeyEncrAlgorithm.Rsa_Kem, HashAlgorithm.Sha256, ' Cms.EnvDataOptions.FormatBase64); ' Console.WriteLine("Cms.MakeEnvDataFromString returns {0} (expecting 1)", n); ' Debug.Assert(1 == n, "Cms.MakeEnvDataFromString failed"); ' ' // Query the enveloped-data file (note flag for isBase64) ' query = "keyEncryptionAlgorithm"; ' s = Cms.QueryEnvData(fname, query, true); ' Console.WriteLine("{0}='{1}'", query, s); ' query = "contentEncryptionAlgorithm"; ' s = Cms.QueryEnvData(fname, query, true); ' Console.WriteLine("{0}='{1}'", query, s); ' ' // Now try and read it using Bob's private key ' sbPrivateKey = Rsa.ReadEncPrivateKey("BobPrivRSAEncrypt.epk", "password"); ' Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key"); ' s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString(), Cms.Options.FormatBase64); ' Wipe.String(sbPrivateKey); ' Console.WriteLine("MSG={0}", s); ' Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed"); ' * -----END WITHDRAWN CODE----- ' Console.WriteLine("") Console.WriteLine("STRONGER RSA KEY ENCRYPTION:") ' Read in Carl's key fname = "CarlPrivRSASign.epk" Console.WriteLine("Reading private key file {0}", fname) sbPrivateKey = Rsa.ReadEncPrivateKey(fname, "password") Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Carl's private key") k = Rsa.KeyHashCode(sbPrivateKey.ToString()) Console.WriteLine("Existing KeyHashCode= {0,8:X}", k) ' Now save again in a new file but using stronger encryption ' (pity about the strength of the password!) fname = "Carl_aes256sha256.epk.txt" n = Rsa.SaveEncPrivateKey(fname, sbPrivateKey.ToString(), 3000, "password", CipherAlgorithm.Aes256, HashAlgorithm.Sha256, _ Rsa.Format.PEM) Wipe.[String](sbPrivateKey) Debug.Assert(0 = n, "Rsa.SaveEncPrivateKey failed") Console.WriteLine("Created new encrypted private key file '{0}'", fname) ' Check we can read in the new format sbPrivateKey = Rsa.ReadEncPrivateKey(fname, "password") Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Carl's private key") Console.WriteLine("Key length={0} bits", Rsa.KeyBits(sbPrivateKey.ToString())) Console.WriteLine("Recreated KeyHashCode={0,8:X}", Rsa.KeyHashCode(sbPrivateKey.ToString())) Debug.Assert(k = Rsa.KeyHashCode(sbPrivateKey.ToString()), "KeyHashCodes do not match") Wipe.[String](sbPrivateKey) ' **** [2008-05-03] WE MESSED UP THE FLAGS FOR PKI_KEYGEN_INDICATE and PKI_BC_TDEA Console.WriteLine("GENERATE NEW RSA KEY PAIR WITH STRONGER ENCRYPTION - TEST FUDGE FOR TDEA:") ' Generate another new RSA key using stronger encryption algorithm for private key pubkeyFile = "test_tdea_pub.bin" prikeyFile = "test_tdea_epk.bin" n = Rsa.MakeKeys(pubkeyFile, prikeyFile, 512, Rsa.PublicExponent.Exp_EQ_65537, 3000, "password", _ 0, HashAlgorithm.Sha1, Rsa.Format.Binary, False) Console.WriteLine("Rsa.MakeKeys returned {0}", n) Debug.Assert(n = 0, "Failed to create RSA key pair") Console.WriteLine("Created public/private key pair OK") ' And check the key pair we just made sbPublicKey = Rsa.ReadPublicKey(pubkeyFile) sbPrivateKey = Rsa.ReadEncPrivateKey(prikeyFile, "password") Console.WriteLine("Key length={0} bits", Rsa.KeyBits(sbPublicKey.ToString())) Console.WriteLine("Key length={0} bytes", Rsa.KeyBytes(sbPrivateKey.ToString())) n = Rsa.KeyMatch(sbPrivateKey.ToString(), sbPublicKey.ToString()) Console.WriteLine("Rsa.KeyMatch returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Rsa.KeyMatch failed.") Console.WriteLine("GENERATE NEW RSA KEY PAIR WITH STRONGER ENCRYPTION:") ' Generate another new RSA key using stronger encryption algorithm for private key ' CAUTION: we make this quite small (only 768 bits) for speed in testing; ' this is too small for real use! pubkeyFile = "carol_pub.pem.txt" prikeyFile = "carol_epk.pem.txt" n = Rsa.MakeKeys(pubkeyFile, prikeyFile, 768, Rsa.PublicExponent.Exp_EQ_65537, 3000, "password", _ CipherAlgorithm.Aes128, HashAlgorithm.Sha224, Rsa.Format.PEM, True) Console.WriteLine("Rsa.MakeKeys returned {0}", n) Debug.Assert(n = 0, "Failed to create RSA key pair") Console.WriteLine("Created public/private key pair OK") ' And check the key pair we just made sbPublicKey = Rsa.ReadPublicKey(pubkeyFile) sbPrivateKey = Rsa.ReadEncPrivateKey(prikeyFile, "password") Console.WriteLine("Key length={0} bits", Rsa.KeyBits(sbPublicKey.ToString())) Console.WriteLine("Key length={0} bytes", Rsa.KeyBytes(sbPrivateKey.ToString())) n = Rsa.KeyMatch(sbPrivateKey.ToString(), sbPublicKey.ToString()) Console.WriteLine("Rsa.KeyMatch returns {0} (expecting 0)", n) Debug.Assert(0 = n, "Rsa.KeyMatch failed.") Console.WriteLine("") Console.WriteLine("STRONGER X.509 SIGNATURE ALGORITHMS:") ' Create a new self-signed CA certificate for Carol issuerCert = "carolSelf_384.cer" distname = "CN=Carol;O=Test" Console.WriteLine("Creating new self-signed X.509 cert '{0}' for '{1}'" & " signed by private key in '{2}'", issuerCert, distname, prikeyFile) n = X509.MakeCertSelf(issuerCert, prikeyFile, &H1, 10, distname, "", _ X509.KeyUsageOptions.KeyCertSign Or X509.KeyUsageOptions.DigitalSignature Or X509.KeyUsageOptions.CrlSign Or X509.KeyUsageOptions.DataEncipherment, "password", X509.Options.SigAlg_Sha384WithRSAEncryption) Debug.Assert(0 = n, "X509.MakeCertSelf failed.") Console.WriteLine("Created self-signed X.509 cert '{0}'", issuerCert) ' Query the new certificate query = "signatureAlgorithm" s = X509.QueryCert(issuerCert, query) Console.WriteLine("{0}='{1}'", query, s) query = "subjectName" s = X509.QueryCert(issuerCert, query) Console.WriteLine("{0}='{1}'", query, s) query = "serialNumber" s = X509.QueryCert(issuerCert, query) Console.WriteLine("{0}='{1}'", query, s) query = "subjectName" s = X509.QueryCert(issuerCert, query) Console.WriteLine("{0}='{1}'", query, s) ' Get the key usage flags k = X509.KeyUsageFlags(issuerCert) ' (Note silly efforts required to get leading zeroes displayed in X format) Console.WriteLine("KeyUsage={0}", k.ToString("X").PadLeft(8, "0"c)) ' Now let's create an X.509 certificate for Bob with a stronger signature algorithm ' To do that we need the issuer's encrypted private key file (which we just made) ' and the subject's public key file (which we don't have). So, for this example, ' we'll just create a new public key file from the key in Bob's existing certificate ' and use the same KeyUsage flags from the existing certificate ' (just because it's easier for testing). k = X509.KeyUsageFlags("BobRSASignByCarl.cer") Console.WriteLine("Bob's key usage flags are {0}", k.ToString("X").PadLeft(8, "0"c)) sbPublicKey = Rsa.GetPublicKeyFromCert("BobRSASignByCarl.cer") Debug.Assert(sbPublicKey.ToString().Length > 0, "Unable to read Bob's public key from certificate") pubkeyFile = "Bob_pubkey.dat" ' And we'll save the public key in SSL format, just for fun :-) n = Rsa.SavePublicKey(pubkeyFile, sbPublicKey.ToString(), Rsa.Format.SSL) Debug.Assert(0 = n, "Rsa.SavePublicKey failed.") ' Make the new certificate, using the new key files distname = "CN=Bob" fnameCert = "BobByCarol_256.cer" Console.WriteLine("Creating new X.509 cert '{0}' for '{1}' using public key in '{2}'" & " signed by private key in '{3}'", fnameCert, distname, pubkeyFile, prikeyFile) n = X509.MakeCert(fnameCert, issuerCert, pubkeyFile, prikeyFile, &H256, 9, _ distname, "", CType(k, X509.KeyUsageOptions), "password", X509.Options.SigAlg_Sha256WithRSAEncryption) Debug.Assert(0 = n, "X509.MakeCert failed.") Console.WriteLine("Created X.509 cert '{0}'", fnameCert) ' Query the new certificate query = "signatureAlgorithm" s = X509.QueryCert(fnameCert, query) Console.WriteLine("{0}='{1}'", query, s) query = "issuerName" s = X509.QueryCert(fnameCert, query) Console.WriteLine("{0}='{1}'", query, s) query = "serialNumber" s = X509.QueryCert(fnameCert, query) Console.WriteLine("{0}='{1}'", query, s) query = "subjectName" s = X509.QueryCert(fnameCert, query) Console.WriteLine("{0}='{1}'", query, s) ' And verify the issuer n = X509.VerifyCert(fnameCert, issuerCert) Console.WriteLine("X509.VerifyCert returns {0} (expecting 0)", n) Debug.Assert(0 = n, "X509.VerifyCert failed.") ' OK, as tricky as we can think of... Console.WriteLine("") Console.WriteLine("MAKE ENVELOPED-DATA USING PEM-STYLE STRINGS FOR CERTS:") ' First, we'll re-save a couple of X.509 certificates in PEM format, ' then we'll read these PEM files (which are text) into strings. fname = "CarolSelf_384.cer" s = X509.ReadStringFromFile(fname) fname = fname & ".pem.txt" n = X509.SaveFileFromString(fname, s, True) Console.WriteLine("Created {0}", fname) cert1 = ReadATextFile(fname) fname = "BobByCarol_256.cer" s = X509.ReadStringFromFile(fname) fname = fname & ".pem.txt" n = X509.SaveFileFromString(fname, s, True) Console.WriteLine("Created {0}", fname) cert2 = ReadATextFile(fname) ' Create an enveloped-data object for Carol and Bob using the PEM cert strings we just read certList = cert1 & ";" & cert2 fname = "cms2carol_bob.p7m" s = "Hello all. Here is a secret message." Console.WriteLine("About to create enveloped-data file '{0}'", fname) n = Cms.MakeEnvDataFromString(fname, s, certList, CipherAlgorithm.Aes128, Cms.KeyEncrAlgorithm.Rsa_Pkcs1v1_5, 0, _ 0) ' Expecting 2 = number of valid recipients Console.WriteLine("Cms.MakeEnvDataFromString returns {0} (expecting 2)", n) Debug.Assert(2 = n, "Cms.MakeEnvDataFromString failed.") ' Check the details in the new CMS file query = "contentEncryptionAlgorithm" s = Cms.QueryEnvData(fname, query, False) Console.WriteLine("{0}='{1}'", query, s) query = "countOfRecipientInfos" s = Cms.QueryEnvData(fname, query, False) Console.WriteLine("{0}='{1}'", query, s) ' NB always returns a string, so convert to an integer n = Convert.ToInt32(s) ' Loop through all recipients For i = 1 To n Console.WriteLine("For recipient {0}:", i) query = [String].Concat("recipientIssuerName", "/", i) s = Cms.QueryEnvData(fname, query, False) Console.WriteLine("{0}='{1}'", query, s) query = [String].Concat("recipientSerialNumber", "/", i) s = Cms.QueryEnvData(fname, query, False) Console.WriteLine("{0}='{1}'", query, s) Next ' Carol reads in her encrypted private key data to a string ' (NB this is not the same as an "internal" key string) Console.WriteLine("Reading file '{0}'", prikeyFile) s = ReadATextFile(prikeyFile) ' so instead of using the filename, we can read this string instead ' [new feature v3.2] sbPrivateKey = Rsa.ReadEncPrivateKey(s, "password") Debug.Assert(sbPrivateKey.Length > 0, "Rsa.ReadEncPrivateKey failed.") Console.WriteLine("Key length={0} bits", Rsa.KeyBits(sbPrivateKey.ToString())) ' Now we have the private key as an internal string (actually StringBuilder) ' Carol can read the message sent to her in the enveloped-data file s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString(), 0) Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed.") Console.WriteLine("Msg='{0}'", s) ' Clean up Wipe.[String](sbPrivateKey) ' ************** ' KEY WRAPPING ' ************** Console.WriteLine("") Console.WriteLine("KEY WRAPPING WITH BLOCK CIPHER:") ' Some test AES-128 key material data arrPlain = Cnv.FromHex("00112233 44556677 8899aabb ccddeeff") Console.WriteLine("Key material={0}", Cnv.ToHex(arrPlain)) ' Key Encryption Key arrKey = Cnv.FromHex("c17a44e8 e28d7d64 81d1ddd5 0a3b8914") Console.WriteLine("KEK ={0}", Cnv.ToHex(arrKey)) ' Wrap with AES128-Wrap algorithm Console.WriteLine("Using AES128-Wrap") arrCipher = Cipher.KeyWrap(arrPlain, arrKey, CipherAlgorithm.Aes128) Debug.Assert(arrCipher.Length > 0, "Cipher.KeyWrap failed.") Console.WriteLine("Wrapped Key ={0}", Cnv.ToHex(arrCipher)) ' Check we can unwrap it bcheck = Cipher.KeyUnwrap(arrCipher, arrKey, CipherAlgorithm.Aes128) Debug.Assert(arrCipher.Length > 0, "Cipher.KeyUnwrap failed.") Console.WriteLine("Check ={0}", Cnv.ToHex(bcheck)) Debug.Assert(CompareByteArrays(bcheck, arrPlain), "Unwrapped key is different.") ' [RSA-KEM withdrawn in v3.4] ' * -----BEGIN WITHDRAWN CODE----- ' Console.WriteLine(""); ' Console.WriteLine("KEY WRAPPING WITH RSA-KEM:"); ' // We use the same key material ' Console.WriteLine("Key material={0}", Cnv.ToHex(arrPlain)); ' // but this time wrap using RSA-KEM with Bob's public key ' fnameCert = "BobRSASignByCarl.cer"; ' Console.WriteLine("Reading public key from '{0}'", fnameCert); ' sbPublicKey = Rsa.GetPublicKeyFromCert(fnameCert); ' Debug.Assert(sbPublicKey.Length > 0, "Rsa.GetPublicKeyFromCert failed."); ' Console.WriteLine("Bob's public key length={0} bits", Rsa.KeyBits(sbPublicKey.ToString())); ' Console.WriteLine("Using RSA-KEM with AES128 and SHA-224"); ' arrCipher = Rsa.KemWrap(arrPlain, sbPublicKey.ToString(), CipherAlgorithm.Aes128, HashAlgorithm.Sha224); ' Debug.Assert(arrCipher.Length > 0, "Rsa.KemWrap failed."); ' Console.WriteLine("Wrapped Key ={0}", Cnv.ToHex(arrCipher)); ' // Check we can unwrap it using bob's private key ' fname = "BobPrivRSAEncrypt.epk"; ' sbPrivateKey = Rsa.ReadEncPrivateKey(fname, "password"); ' Debug.Assert(sbPrivateKey.Length > 0, "Rsa.ReadEncPrivateKey failed."); ' bcheck = Rsa.KemUnwrap(arrCipher, sbPrivateKey.ToString(), CipherAlgorithm.Aes128, HashAlgorithm.Sha224); ' Debug.Assert(arrCipher.Length > 0, "Rsa.KemUnwrap failed."); ' Console.WriteLine("Check ={0}", Cnv.ToHex(bcheck)); ' Debug.Assert(CompareByteArrays(bcheck, arrPlain), "Unwrapped key is different."); ' * -----END WITHDRAWN CODE----- ' ' ********************* ' AUTACK SIGNATURES ' ********************* ' (This is specialist stuff) Console.WriteLine("") Console.WriteLine("AUTACK SIGNATURES:") ' Ref: EDIFACT, Section 12.9 Worked Examples ' Short RSA private key in XML format -- this is NOT SECURE! xmlKey = "<RSAKeyPair>" & "<Modulus EncodingType='hexBinary'>" & "A59FBFE322244760E5B430B197967FDCF240D6B134D0F3783EDE652B565AC9C6" & "105768F11EE59AED359ED6DB6CE6AFC84233F35B60895B90AF85A66E598C15CE" & "FB860EA37CCEDB4A45B4C0594974EB76BC955C43B56B17940DFDCAB3E0C03F49" & "A308835772405E74085BF59FBA726969CBE348DAB6F9456DA57B40B64E6E3C65</Modulus>" & "<Exponent EncodingType='hexBinary'>010001</Exponent>" & "<D EncodingType='hexBinary'>" & "244FFDD97DED0FD4441089638A7D85FBAA86823BB87D7E7FF4E2BC322FFCF843" & "AB660ABD60DD8CE5E8AD72648A001AF6B06324FE3A106B89B19DFF232F116A5F" & "4C7151C38D4A132E0EEBC55EC0DC44EBE0CCBA8FCACB6C93F32997DAD8B9ACEA" & "B4BEC5D4A1F38A2218337C8C92D301C433A7E02EB4A456F5DE83AE1AD76E3D31</D>" & "</RSAKeyPair>" msgStr = "abc" ' The correct answer strCheck = "4897C41FFCB27C4B77F0711890C5C48E9C42AE5A1548E1A4653CDF444C60350F" & "635A16393D5862DCBD83EF3727435B750CE889EB3C48C02EA0B14F6F6B4BA0D1" & "E16A010D42830110AB36AB183F2976B784656D4272A6215A44EAA504610C59AC" & "C615E661BE4EC5ACE09B8D9DCE165F0CE71AE8743266ED2F20F35862B3C9252D" keyStr = Rsa.FromXMLString(xmlKey, False) keyBits = Rsa.KeyBits(keyStr) Console.WriteLine("Private key is {0} bits long", keyBits) ' Compute message digest of data hexDigest = Hash.HexFromString(msgStr, HashAlgorithm.Sha1) Console.WriteLine("Message-to-encode=Digest={0}", hexDigest) ' Convert digest to byte array msg = Cnv.FromHex(hexDigest) ' Encode digest using ISO-9796-1 b = Rsa.EncodeMsgIso9796(msg, keyBits) Debug.Assert(b.Length > 0, "Failed to Encode for ISO9796-1") Console.WriteLine("Encoded block=" & vbLf & "{0}", Cnv.ToHex(b)) ' Sign block with RSA private key ' -- use special RSA2 method with magic nibble value 6 b = Rsa.RawPrivate(b, keyStr, &H6) ' Convert to hex encoding s = Cnv.ToHex(b) Console.WriteLine("Autack Signature=" & vbLf & "{0}", s) Debug.Assert(String.Compare(s, strCheck, True) = 0, "Autack signature is wrong") ' Now we verify the Autack signature using the public key xmlKey = "<RSAKeyValue>" & "<Modulus EncodingType='hexBinary'>" & "A59FBFE322244760E5B430B197967FDCF240D6B134D0F3783EDE652B565AC9C6" & "105768F11EE59AED359ED6DB6CE6AFC84233F35B60895B90AF85A66E598C15CE" & "FB860EA37CCEDB4A45B4C0594974EB76BC955C43B56B17940DFDCAB3E0C03F49" & "A308835772405E74085BF59FBA726969CBE348DAB6F9456DA57B40B64E6E3C65</Modulus>" & "<Exponent EncodingType='hexBinary'>010001</Exponent>" & "</RSAKeyValue>" ' Read in the public key from the XML string keyStr = Rsa.FromXMLString(xmlKey, True) keyBits = Rsa.KeyBits(keyStr) Console.WriteLine("Public key is {0} bits long", keyBits) ' Convert the hex singature string to a byte array b = Cnv.FromHex(s) ' Decrypt the signature block using the public key ' -- use special RSA2 method with magic nibble value 0x6 b = Rsa.RawPublic(b, keyStr, &H6) Console.WriteLine("Decrypted block=" & vbLf & "{0}", Cnv.ToHex(b)) ' Recover the message from the block using the ISO9796-1 method msg = Rsa.DecodeMsgIso9796(b, keyBits) Debug.Assert(msg.Length > 0, "Failed to Decode for ISO9796-1") s = Cnv.ToHex(msg) Console.WriteLine("Recovered message='{0}'", s) ' This should be equal to the message digest we prepared earlier Debug.Assert(String.Compare(s, hexDigest, True) = 0, "Recovered message is wrong") ' ********************* ' PEM FILE CONVERSIONS ' ********************* Console.WriteLine("") Console.WriteLine("PEM FILE CONVERSIONS:") fnameInput = "AliceRSASignByCarl.cer" fnameOutput = "AliceRSASignByCarl.pem.cer" ' Make a PEM file from a binary one r = Pem.FileFromBinFile(fnameOutput, fnameInput, "CERTIFICATE", 72) Debug.Assert(r = 0, "Pem.FileFromBinFile failed.") Console.WriteLine("Created new PEM file {0}", fnameOutput) ' Read and display s = ReadATextFile(fnameOutput) Console.WriteLine("{0}", s) ' Check these two certs have the same thumbprint hexDigest = X509.CertThumb(fnameOutput, HashAlgorithm.Sha1) Console.WriteLine("{1}=SHA-1({0})", fnameOutput, hexDigest) strCheck = hexDigest hexDigest = X509.CertThumb(fnameInput, HashAlgorithm.Sha1) Console.WriteLine("{1}=SHA-1({0})", fnameInput, hexDigest) Debug.Assert(strCheck = hexDigest, "Digests are not equal") ' Now make a new binary file back from the PEM file fnameInput = fnameOutput fnameOutput = "AliceRSASignByCarl-dup.cer" r = Pem.FileToBinFile(fnameOutput, fnameInput) Debug.Assert(r = 0, "Pem.FileToBinFile failed.") Console.WriteLine("Created binary file {0}", fnameOutput) ' Check we have the same thumbprint hexDigest = X509.CertThumb(fnameOutput, HashAlgorithm.Sha1) Console.WriteLine("{1}=SHA-1({0})", fnameOutput, hexDigest) Debug.Assert(strCheck = hexDigest, "Digests are not equal") ' ************************* ' PADDING FOR BLOCK CIPHERS ' ************************* Console.WriteLine("") Console.WriteLine("PADDING FOR BLOCK CIPHERS:") ' 1. Pad a byte array of 5 bytes for Triple DES ECB/CBC mode b = New Byte(4) {&HFF, &HFF, &HFF, &HFF, &HFF} Console.WriteLine("Input =0x{0}", Cnv.ToHex(b)) b = Cipher.Pad(b, CipherAlgorithm.Tdea) Console.WriteLine("Padded =0x{0}", Cnv.ToHex(b)) ' Now strip the padding b = Cipher.Unpad(b, CipherAlgorithm.Tdea) Console.WriteLine("Unpadded=0x{0}", Cnv.ToHex(b)) ' 2. Pad a byte array of 18 bytes for AES ECB/CBC mode b = New Byte(17) {} For i = 0 To b.Length - 1 b(i) = &HFF Next Console.WriteLine("Input =0x{0}", Cnv.ToHex(b)) b = Cipher.Pad(b, CipherAlgorithm.Aes128) Console.WriteLine("Padded =0x{0}", Cnv.ToHex(b)) ' Now strip the padding b = Cipher.Unpad(b, CipherAlgorithm.Aes128) Console.WriteLine("Unpadded=0x{0}", Cnv.ToHex(b)) ' 3. Pad an empty byte array for TDEA b = New Byte(-1) {} Console.WriteLine("Input =0x{0}", Cnv.ToHex(b)) b = Cipher.Pad(b, CipherAlgorithm.Tdea) Console.WriteLine("Padded =0x{0}", Cnv.ToHex(b)) ' Now strip the padding b = Cipher.Unpad(b, CipherAlgorithm.Tdea) Console.WriteLine("Unpadded=0x{0}", Cnv.ToHex(b)) ' 4. Pad a hex string for AES s = "ff" Console.WriteLine("Input ='{0}'", s) s = Cipher.Pad(s, CipherAlgorithm.Aes128) Console.WriteLine("Padded ='{0}'", s) ' Now strip the padding s = Cipher.Unpad(s, CipherAlgorithm.Aes128) Console.WriteLine("Unpadded='{0}'", s) ' 5. Pad an empty hex string for AES s = "" Console.WriteLine("Input ='{0}'", s) s = Cipher.Pad(s, CipherAlgorithm.Aes128) Console.WriteLine("Padded ='{0}'", s) ' Now strip the padding s = Cipher.Unpad(s, CipherAlgorithm.Aes128) Console.WriteLine("Unpadded='{0}'", s) '************************** ' BASE64 CONVERSION TESTS * '************************** ' New in [v3.6] Console.WriteLine("") Console.WriteLine("BASE64 CONVERSION TESTS:") ' 0xFEDCBA9876543210 in base64 b64str = "/ty6mHZUMhA=" b = Cnv.FromBase64(b64str) Console.WriteLine("'{0}'=0x{1}", b64str, Cnv.ToHex(b)) ' Back to base64 s = Cnv.ToBase64(b) Console.WriteLine("In base64='{0}'", s) ' Directly to hex Console.WriteLine("In hex='{0}'", Cnv.HexFromBase64(b64str)) ' hex -> base64 s = "FEDCBA9876543210" b64str = Cnv.Base64FromHex(s) Console.WriteLine("0x{0}='{1}'", s, b64str) ' string -> base64 s = "México" b64str = Cnv.ToBase64(s) ' base64 -> hex Console.WriteLine("'{0}'=>'{1}'", s, b64str) Console.WriteLine("In hex='{0}'", Cnv.HexFromBase64(b64str)) ' base64 -> string s = Cnv.StringFromBase64(b64str) Console.WriteLine("'{0}'=>'{1}'", b64str, s) ' Filter out non-base64 chars (%.!*&) and e-acute s = "%/ty6m.!*HZUMhA=&é" b64str = Cnv.Base64Filter(s) Console.WriteLine("Filter: '{0}'=>'{1}'=>0x{2}", s, b64str, Cnv.HexFromBase64(b64str)) Debug.Assert(Cnv.HexFromBase64(b64str) = "FEDCBA9876543210", "Cnv.FilterBase64 failed") '******************* ' CONVERSION TESTS * '******************* Console.WriteLine("") Console.WriteLine("OTHER CONVERSION TESTS:") ' Create a string that includes some Latin-1 accented characters: s = "abcóéíáñ" ' This should contain the following 8 characters: ' U+0061 LATIN SMALL LETTER A ' U+0062 LATIN SMALL LETTER B ' U+0063 LATIN SMALL LETTER C ' U+00F3 LATIN SMALL LETTER O WITH ACUTE ' U+00E9 LATIN SMALL LETTER E WITH ACUTE ' U+00ED LATIN SMALL LETTER I WITH ACUTE ' U+00E1 LATIN SMALL LETTER A WITH ACUTE ' U+00F1 LATIN SMALL LETTER N WITH TILDE Console.WriteLine("Use GetBytes to convert a string to bytes...") Console.WriteLine("Test string='{0}' ({1} chars)", s, s.Length) Debug.Assert(8 = s.Length, "String must be 8 chars for these tests") Console.WriteLine("Use GetBytes in 'Default' mode:") b = System.Text.Encoding.[Default].GetBytes(s) Console.WriteLine("Default.GetBytes=>{0} ({1} bytes)", Cnv.ToHex(b), b.Length) Console.WriteLine("Use GetBytes in 'Latin-1' mode. Three alternatives:") b = System.Text.Encoding.GetEncoding("iso-8859-1").GetBytes(s) Console.WriteLine("GetEncoding(""iso-8859-1"").GetBytes=>{0} ({1} bytes)", Cnv.ToHex(b), b.Length) ' Code page 28591 == iso-8859-1 b = System.Text.Encoding.GetEncoding(28591).GetBytes(s) Console.WriteLine("GetEncoding(28591).GetBytes=>{0} ({1} bytes)", Cnv.ToHex(b), b.Length) ' Note: Windows-1252 is *almost* the same as ISO-8859-1 b = System.Text.Encoding.GetEncoding(1252).GetBytes(s) Console.WriteLine("GetEncoding(1252).GetBytes =>{0} ({1} bytes)", Cnv.ToHex(b), b.Length) Console.WriteLine("But in UTF-8 the bytes are different:") b = System.Text.Encoding.UTF8.GetBytes(s) Console.WriteLine("UTF8.GetBytes=>{0} ({1} bytes)", Cnv.ToHex(b), b.Length) ' Check that byte array contains valid UTF-8 characters b = System.Text.Encoding.UTF8.GetBytes(s) n = Cnv.CheckUTF8(b) Console.WriteLine("Cnv.CheckUTF8(b)={0} (expected 2)", n) Debug.Assert(2 = n, "Expected return value 2") ' Test a file instead of a byte array [New in v3.7] fname = "test-utf8.txt" MakeABinaryFile(fname, b) n = Cnv.CheckUTF8File(fname) Console.WriteLine("Cnv.CheckUTF8File({1})={0} (expected 2)", n, fname) Debug.Assert(2 = n, "Expected return value 2") ' AN ASIDE TO DO SOME TESTS ON THE BOUNDARIES... ' Test subtle differences between ISO-8859-1 and WINDOWS-1252 ' Create a byte array representing some characters in ISO-8859-1 "hole" b1 = New Byte() {&H61, &H93, &H86, &H87, &H8C, &H94, _ &H62} Console.WriteLine("Show difference between ISO-8859-1 and Windows-1252 for chars 0x80 to 0x9E...") Console.WriteLine("Test bytes for GetString={0}", Cnv.ToHex(b1)) s1 = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(b1) Console.WriteLine("s1(iso-8859-1)='{0}'", s1) s1 = System.Text.Encoding.GetEncoding(1252).GetString(b1) Console.WriteLine("s1(Windows-1252)='{0}'", s1) ' Check code page values Dim encod As Encoding = Encoding.[Default] Console.WriteLine("Default code page={0} (usually 1252, but might not be)", encod.CodePage) encod = Encoding.GetEncoding("iso-8859-1") Console.WriteLine("iso-8859-1 code page={0} (expecting 28591)", encod.CodePage) ' Try converting invalid UTF-8 bytes Console.WriteLine("Try passing invalid UTF-8 bytes to UTF8.GetString...") b1 = New Byte() {&HEF, &HBF, &HBF} Console.WriteLine("Pass illegal UTF-8 bytes to UTF8.GetString <- 0x{0}", Cnv.ToHex(b1)) s1 = System.Text.Encoding.UTF8.GetString(b1) Console.WriteLine("s1(UTF8)='{0}' Cnv.CheckUTF={1} (0=>invalid UTF-8)", s1, Cnv.CheckUTF8(b1)) b1 = New Byte() {&HC3, &HB3, &HC3, &HA9, &HC3, &HAD, _ &HC3, &HA1, &HC3} Console.WriteLine("Pass chopped UTF-8 bytes to UTF8.GetString <- 0x{0}", Cnv.ToHex(b1)) s1 = System.Text.Encoding.UTF8.GetString(b1) Console.WriteLine("s1(UTF8)='{0}' Cnv.CheckUTF={1}", s1, Cnv.CheckUTF8(b1)) ' CONVERT OUR BYTE ARRAY FROM THE TEST STRING BACK TO A STRING TYPE Console.WriteLine("Use GetString to convert bytes back to a string...") ' b contains test string encoded into UTF-8 bytes ' First we do the right thing s1 = System.Text.Encoding.UTF8.GetString(b) Console.WriteLine("UTF8.GetString(b)='{0}' (should display OK with accents and tilde)", s1) n = Cnv.CheckUTF8(s) Console.WriteLine("Cnv.CheckUTF8(s) returns {0} (expected 0 => not valid UTF-8)", n) ' Then we force "UTF-8" bytes into a "Latin-1" string which will print "funny" s1 = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(b) Console.WriteLine("(""iso-8859-1"").GetString(b)='{0}' (should show 'funny' characters)", s1) n = Cnv.CheckUTF8(s) Console.WriteLine("Cnv.CheckUTF8(string) returns {0} (expected 0=>invalid)", n) n = Cnv.CheckUTF8(b) Console.WriteLine("Cnv.CheckUTF8(bytes) returns {0} (expected 2 => Valid UTF-8 w/8-bit ANSI)", n) ' Use byte-to-byte encoding conversions Console.WriteLine("Use Cnv.ByteEncoding to convert bytes between different encodings...") ' b contains UTF-8-encoded data, so convert to Latin-1 b1 = Cnv.ByteEncoding(b, Cnv.EncodingConversion.Latin1_From_Utf8) Console.Write("Cnv.ByteEncoding(Latin1_From_Utf8): ") Console.WriteLine("{0} ({1} bytes)", Cnv.ToHex(b), b.Length) Console.WriteLine(" --> {0} ({1} bytes)", Cnv.ToHex(b1), b1.Length) Debug.Assert(8 = b1.Length, "Invalid conversion: expected 8 bytes") n = Cnv.CheckUTF8(b1) Console.WriteLine("Cnv.CheckUTF8(b1) returns {0} (expected 0 => not valid UTF-8)", n) Debug.Assert(0 = n, "Cnv.CheckUTF8(b1) failed") ' and back to UTF-8 b = Cnv.ByteEncoding(b1, Cnv.EncodingConversion.Utf8_From_Latin1) Console.Write("Cnv.ByteEncoding(Utf8_From_Latin1): ") Console.WriteLine("{0} ({1} bytes)", Cnv.ToHex(b1), b1.Length) Console.WriteLine(" --> {0} ({1} bytes)", Cnv.ToHex(b), b.Length) Debug.Assert(13 = b.Length, "Invalid conversion: expected 13 bytes") n = Cnv.CheckUTF8(b) Console.WriteLine("Cnv.CheckUTF8(b) returns {0} (expected 2 => Valid UTF-8 w/8-bit ANSI)", n) Debug.Assert(2 = n, "Cnv.CheckUTF8(b) failed") ' Do test in the documentation that fails b = Cnv.FromHex("61E962") ' A valid byte array representing the Latin-1 string "aéb" but invalid UTF-8 b1 = Cnv.ByteEncoding(b, Cnv.EncodingConversion.Latin1_From_Utf8) n = General.ErrorCode() ' NB remember ErrorCode before we call Cnv that clears it! Console.WriteLine("Cnv.ByteEncoding(0x{0},Latin1_From_Utf8) = '{1}' (expected empty result)", Cnv.ToHex(b), Cnv.ToHex(b1)) Console.WriteLine("General.ErrorCode={0} {1} (expected error here)", n, General.ErrorLookup(n)) ' HEX CONVERSIONS Console.WriteLine("Show the Cnv.FromHex method will filter out non-hex characters...") ' Convert a hex string containing ":" characters -> 16 bytes s = "30:21:30:09:06:05:2B:0E:03:02:1A:05:00:04:14:00" b = Cnv.FromHex(s) Console.WriteLine("Cnv.FromHex('{0}')=>", s) Console.WriteLine("...to give a valid byte array:") Console.WriteLine("{0} ({1} bytes)", Cnv.ToHex(b), b.Length) Debug.Assert(16 = b.Length, "Cnv.FromHex failed: wrong # of bytes") ' ******************************************** ' FINALLY, PRINT OUT DETAILS OF CORE DLL AGAIN ' ******************************************** Console.WriteLine("") Console.WriteLine("Details of core DLL...") n = General.Version() ch = General.LicenceType() s = General.Platform() Console.WriteLine("Version={0}, LicenceType={1}, Platform={2}", n, ch, s) s = General.ModuleName() Console.WriteLine("ModuleName={0}", s) s = General.CompileTime() Console.WriteLine("CompileTime={0}", s) '******************************************** Console.WriteLine(vbLf & "ALL TESTS COMPLETED.") '********************************************************* ' Put CWD back to parent and offer to remove the test dir '********************************************************* System.IO.Directory.SetCurrentDirectory("..") Console.WriteLine(vbLf & "CWD reset to " & System.IO.Directory.GetCurrentDirectory()) If askDelete Then Console.Write("The temp test directory '{0}' was created by this program." & vbLf & "Do you want to remove it? ([Y]/N) ", subdir) s = Console.ReadLine() If "N" <> s AndAlso "n" <> s Then ' Remove directory Console.WriteLine("Removing test directory...") System.IO.Directory.Delete(subdir, True) Else Console.WriteLine("Temp directory '{0}' left in place.", subdir) End If Else ' Remove directory regardless Console.WriteLine("Removing test directory '{0}'", subdir) System.IO.Directory.Delete(subdir, True) End If End Sub '***************** ' FILE UTILITIES * '***************** Private Shared Function MakeATextFile(fileName As String, data As String) As Boolean Dim fs As FileStream Dim sw As StreamWriter fs = New FileStream(fileName, FileMode.Create, FileAccess.Write) sw = New StreamWriter(fs) sw.Write(data) sw.Close() fs.Close() Return True End Function Private Shared Function MakeABinaryFile(fileName As String, data As Byte()) As Boolean Dim fs As FileStream Dim bw As BinaryWriter fs = New FileStream(fileName, FileMode.Create, FileAccess.Write) bw = New BinaryWriter(fs) bw.Write(data) bw.Close() fs.Close() Return True End Function Private Shared Function MakeALargeTextFile(fileName As String) As Long ' Make a large (~10MB) text file. Return length. Dim targetsize As Integer = 1024 * 1024 * 10 Dim nblocks As Integer Dim s As String Dim sb As New StringBuilder() ' Make a string of all printable ASCII chars For i As Integer = 32 To 126 sb.Append(ChrW(i)) Next sb.Append(ControlChars.Cr) sb.Append(ControlChars.Lf) s = sb.ToString() ' Write out the text file nblocks = targetsize \ s.Length Dim fs As FileStream Dim sw As StreamWriter fs = New FileStream(fileName, FileMode.Create, FileAccess.Write) sw = New StreamWriter(fs) For i As Integer = 0 To nblocks - 1 sw.Write(s) Next sw.Close() fs.Close() Return FileLength(fileName) End Function Private Shared Function ReadABinaryFile(fileName As String) As Byte() Dim b As Byte() = New Byte(-1) {} Dim finfo As New FileInfo(fileName) If finfo.Exists Then Dim fsi As FileStream = finfo.OpenRead() Dim br As New BinaryReader(fsi) Dim count As Integer = CInt(fsi.Length) b = br.ReadBytes(count) br.Close() fsi.Close() End If Debug.Assert(finfo.Exists, "File '" & fileName & "' does not exist.") Return b End Function Private Shared Function ReadATextFile(fileName As String) As String Dim s As String = [String].Empty Dim finfo As New FileInfo(fileName) If finfo.Exists Then Dim fsi As FileStream = finfo.OpenRead() Dim sr As New StreamReader(fsi) s = sr.ReadToEnd() 'Console.WriteLine(s); sr.Close() fsi.Close() End If Debug.Assert(finfo.Exists, "File '" & fileName & "' does not exist.") Return s End Function Private Shared Function PrintATextFile(fileName As String) As String ' Same as ReadATextFile except it prints to the console Dim s As String = [String].Empty Dim finfo As New FileInfo(fileName) If finfo.Exists Then Dim fsi As FileStream = finfo.OpenRead() Dim sr As New StreamReader(fsi) s = sr.ReadToEnd() Console.WriteLine(s) sr.Close() fsi.Close() End If Return s End Function Private Shared Function FileExists(filePath As String) As Boolean Dim fi As New FileInfo(filePath) Return fi.Exists End Function Private Shared Function FileLength(filePath As String) As Long Dim fi As New FileInfo(filePath) Return fi.Length End Function Private Shared Function FileIsNotPresent(filePath As String, message As String) As Boolean If Not FileExists(filePath) Then Console.WriteLine(vbLf & "{0}: {1}", message, filePath) Return True End If Return False End Function ' Amazingly, .NET doesn't have a Compare Bytes function... Private Shared Function CompareByteArrays(data1 As Byte(), data2 As Byte()) As Boolean ' Thanks to Jon Skeet http://www.pobox.com/~skeet ' If both are null, they're equal If data1 Is Nothing AndAlso data2 Is Nothing Then Return True End If ' If either but not both are null, they're not equal If data1 Is Nothing OrElse data2 Is Nothing Then Return False End If If data1.Length <> data2.Length Then Return False End If For i As Integer = 0 To data1.Length - 1 If data1(i) <> data2(i) Then Return False End If Next Return True End Function End Class End Namespace