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