Imports System
Imports System.Diagnostics
Imports System.Reflection
Imports System.IO
Imports System.Text

Imports CryptoSysPKI

'  $Id: TestPKIcsharp.vb $ 
' *   Last updated:
' *   $Date: 2023-01-01 03:41:00 $
' *   $Version: 21.0.0 $
' 


' Some tests using the CryptoSysPKI .NET interface.
' * 
' * Requires `CryptoSys PKI` to be installed on your system: available from <https://cryptosys.net/pki/>.
' * Add a reference to `diCrSysPKINet.dll` installed in `C:\Program Files (x86)\CryptoSysPKI\DotNet`,
' * or include the C# source code module `CryptoSysPKI.cs` directly in your project.
' * 
' * Test files are in `pkiDotNetTestFiles.zip`. These must be in the CWD.
' *  
' * This is a Console Application written for target .NET Framework 4.0 and above.
' * Please report any bugs to <https://cryptosys.net/contact>.
' 

'****************************** LICENSE ***********************************
' * Copyright (C) 2005-23 David Ireland, DI Management Services Pty Limited.
' * All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
' * The code in this module is licensed under the terms of the MIT license.  
' * For a copy, see <http://opensource.org/licenses/MIT>
'****************************************************************************
'


' [2015-03] Re-organised into modular form 

' [2017-07] Changed license on this code to MIT 

' [2019-03] Updated to avoid deprecated Cms.Options 

' [2021-10] Requires .NET 4.0 



' NOTE: Requires certain files to exist in the current working directory
' * (by default, the same directory as the executable).
' * See `arrFileNames` below - these files are in `pkiDotNetTestFiles.zip`
' 


' Ported from C# to VB.NET using icsharpcode.net's SharpDevelop.

Namespace TestPKIExamples
	''' <summary>
	''' Test examples for CryptoSysPKI interface
	''' </summary>
	Class TestPKIExamples
		Private Const MIN_PKI_VERSION As Integer = 200700

		' Test files required to exist in the current working directory:
		' Unencrypted version 
		' Encrypted, password="password" 

		Private Shared arrFileNames As String() = New String() {"alice-lamps.crt", "alice-lamps.p12", "AlicePrivRSASign.p8e", "AlicePubRSA.pub", "AliceRSASignByCarl.cer", "bob.p7b", _
			"bob-lamps.crt", "bob-lamps.p12", "BobPrivRSAEncrypt.p8e", "BobRSASignByCarl.cer", "CarlPrivRSASign.p8e", "CarlRSASelf.cer", _
			"dims.cer", "ocsp_response_ok_dims.dat", "rfc3280bis_cert1.cer", "rfc3280bis_cert2.cer", "rfc3280bis_CRL.crl", "smallca.cer", _
			"sonnets.txt", "UTNUSERFirst-Object.cer", "AliceRSAPSS.p8", "AliceRSAPSS.p8e", "AliceRSAPssSignByCarl.cer", "BobRSAPSS.p8e", _
			"BobRSAPssSignByCarl.cer", "CarlRSAPssSelf.cer", "CA_ECC_P256.p8e", "CA_ECC_P256.pub", "CA_RSA_2048.p8e", "CA_RSA_2048.pub", _
			"excontent.txt", "rsa-oaep-1.p8", "rsa-oaep-1.pub", "User_ECC_P256.p8e", "User_ECC_P256.pub", "User_RSA_2048.p8e", _
			"User_RSA_2048.pub", "User_RSA_2048.cer", "User2_ECC_P256.p8e", "User2_ECC_P256.pub", "User2_RSA_2048.p8e", "User2_RSA_2048.pub", _
			"edwards-ietf.p8", "edwards-ietf-ex.p8", "Ed25519-ietf-selfsigned.cer", "lamps-alice.crt", "lamps-alice.p8", "lamps-alice.encrypt.crt", _
			"lamps-alice.decrypt.p8.pem", "lamps-dana.encrypt.crt", "lamps-dana.decrypt.p8.pem", "lamps-ca.ed25519.crt", "lamps-ca.ed25519.p8", "lamps-bob.p8.pem", _
			"lamps-bob.crt", "lamps-ca.rsa.p8", "File with a long name and spaces hello there all good yes thanks.txt", "你好.txt"}
		' Name of file with zipped test files
		Friend Shared zippedTestFiles As String = "pkiDotNetTestFiles.zip"

		' Global vars set by command-line args
		Private Shared doPrompt As Boolean = False
		Private Shared doBigFile As Boolean = False


		''' <summary>
		''' The main entry point for the application.
		''' </summary>
		<STAThread> _
		Public Shared Sub Main(args As String())
			Dim origdir As String
			' To store CWD before changed to temp
			' Make sure minimum required version of CryptoSys PKI is installed...
			Console.WriteLine("PKI Version is " & General.Version())
			If General.Version() < MIN_PKI_VERSION Then
				Console.WriteLine("FATAL ERROR: Require PKI version " & MIN_PKI_VERSION & " or later.")
				Return
			End If
			Console.WriteLine("NET version is {0}", General.NetVersion())


			' Handle command-line arguments
'			 * [some]      just do some tests (default = do all)
'			 * [prompt]    prompt for keystrokes and passwords
'			 * [bigfile]   do operations on big files
'			 * [askdelete] ask to delete the temp directory when finished (default=delete automatically)
'			 


			Dim doSome As Boolean = False
			Dim askDelete As Boolean = False
			' These are global
			doPrompt = False
			doBigFile = False
			For iarg As Integer = 0 To args.Length - 1
				If args(iarg) = "some" Then
					doSome = True
				End If
				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

			' Check required files exist and setup temp directory
			origdir = SetupTestFilesAndDirectory(arrFileNames)
			If origdir Is Nothing Then
				Return
			End If


			'*************
			' DO THE TESTS
			'*************
			If doSome Then
				' Use "some" in the command line
				' Do some tests - comment these out as required
				Console.WriteLine("DOING SOME TESTS...")
				'test_General();
				'test_Tdea();
				'test_Rsa();
				'test_Rsa_XML();
				'test_X509();
				'test_Pfx();
				'test_CRL();
				'test_OCSP();
				'test_CMS(doBigFile);
				'test_CMS_EnvData();
				'test_CMS_SMIME_Chain();
				'test_Hash();
				'test_Hmac();
				'test_Prompt(doPrompt);
				'test_Wipe();
				'test_Rng(doPrompt);
				'test_Cipher();
				'test_Cipher_Hex();
				'test_CipherPad();
				'test_KeyWrap();
				'test_Rsa_Keys();
				'test_Rsa_X509_Stronger();
				'test_Autack();
				'test_PEM();
				'test_Padding();
				'test_Cnv_hex();
				'test_Cnv_base64();
				'test_Cnv_other();
				'test_Cnv_base58();
				'test_Pbkdf2();
				'test_Sig();
				'test_Sig_Vars();
				'test_Asn1();
				'test_Byte_Utils();
				'test_Ecc_MakeKeys();
				'test_Ecc_KeyByCurve();
				'test_Ecc_SaveKey();
				'test_Ecc_MakeReadSaveKeys();
				'test_Sig_Ecc();
				'test_Sig_Ecc_det();
				'test_Sig_Ecc_btc();
				'test_Cipher_File();
				'test_Cipher_Size();
				'test_CMS_SigData_PSS();
				'test_Compress();
				'test_Cms_Data_Bytes();
				'test_ReadJWK();
				'test_Rng_Guid();
				'test_Cipher_Encrypt_Prefix();
				'test_X509_MakeCert_EmptyDN();
				'test_X509_CertRequest_EmptyDN_extKeyUsage();
				'test_X509_ReadCertStringFromPFX_3des();
				'test_PFX_MakeFile_3DES();
				'test_CMS_MakeSigData_signingcert();
				' Tests copied from TestPKIcsharpV12.cs 

				'test_ReadRSA_ECCKeys();
				'test_MakeCerts_ECDSA();
				'test_MakeCerts_PSS();
				'test_SIG_VerifyData_PSS();
				'test_RSA_EncryptDecrypt();
				'test_SIG_SignData_PSS();
				'test_SIG_SignData_ECDSA();
				'test_RSA_ToXMLStringEx();
					'test_MakeSignedEnveloped_PSS_OAEP();
					'test_PFX_MakeFile_PSS();
					'test_CIPHER_EncryptAEAD();
					'test_X509_ReadCertFromP7Chain();
					'test_X509_ReadCertFromPFX();
					' New in [v20.0] 

					'test_CIPHER_EncryptHex();
					'test_RSA_XML_withprefixes();
					'test_ECC_DHSharedSecret();
					'test_ECC_DHSharedSecret_25519();
					'test_SIG_SignData_Ed25519();
					'test_X509_MakeCertSelf_Ed25519();
					'test_CMS_MakeSigData_Ed25519();
					' New in [v20.2] 

					'test_General_FormatErrorMessage();
					' New in [v20.3] 

					'test_RSA_SaveEncKey();
					' New in [v20.4] 

					'test_ECC_Brainpool();
					' New in [v20.5] 

					'test_HASH_Length();
					'test_KDF_Bytes();
					'test_KDF_ForCms();
					'test_CMS_EnvData_ecdh();
					'test_X509_MakeCert_Internal_X25519();
					'test_PFX_MakeFile_AES256();
					' New in [v20.6] 

					'test_CMS_EnvData_auth();
					'test_CMS_EnvData_pwri();
					'test_CMS_EnvData_kekri();
					'test_CMS_EnvData_examples();

					' New in [v21.0] 

					'test_Hash_SHA3();
					'test_Hmac_SHA3();
					'test_Xof();
					'test_Prf();
					'test_CIPHER_File_GCM();
					'test_RSA_ReadPublicKey_CSR();
					'test_X509_MakeCert_Ex();
					'test_CNV_ShortNamePath();
					'test_CNV_ShortNamePath_EncryptFile();


				test_CMS_MakeSigData_PSS()
			Else
				' Do all the test modules (default)
				DoAllTests()
			End If


			' FINALLY, DISPLAY QUICK INFO ABOUT THE CORE DLL
			Console.WriteLine(vbLf & "DETAILS OF CORE DLL...")
			Console.WriteLine("DLL Version={0} [{1}] Lic={2} Compiled=[{3}] ", General.Version(), General.Platform(), General.LicenceType(), General.CompileTime())
			Console.WriteLine("[{0}] NetVersion={1}", General.ModuleName(), General.NetVersion())


			'********************************************
			Console.WriteLine(vbLf & "ALL TESTS COMPLETED.")

			' Put CWD back to original and offer to remove the test dir
			RestoreDirectory(origdir, askDelete)

		End Sub

		Private Shared Sub DoAllTests()
			Console.WriteLine("DOING ALL TESTS...")
			test_General()
			test_Tdea()
			test_Rsa()
			test_Rsa_XML()
			test_X509()
			test_Pfx()
			test_CRL()
			test_OCSP()
			test_CMS(doBigFile)
			test_CMS_EnvData()
			test_CMS_SMIME_Chain()
			test_Hash()
			test_Hmac()
			test_Prompt(doPrompt)
			test_Wipe()
			test_Rng(doPrompt)
			test_Cipher()
			test_Cipher_Hex()
			test_CipherPad()
			test_KeyWrap()
			test_Rsa_Keys()
			test_Rsa_X509_Stronger()
			test_Autack()
			test_PEM()
			test_Padding()
			test_Cnv_hex()
			test_Cnv_base64()
			test_Cnv_other()
			test_Cnv_base58()
			test_Pbkdf2()
			test_Sig()
			test_Sig_Vars()
			test_Asn1()
			test_Byte_Utils()
			test_Ecc_MakeKeys()
			test_Ecc_KeyByCurve()
			test_Ecc_SaveKey()
			test_Ecc_MakeReadSaveKeys()
			test_Sig_Ecc()
			test_Sig_Ecc_det()
			test_Sig_Ecc_btc()
			test_Cipher_File()
			test_Cipher_Size()
			test_Compress()
			test_Cms_Data_Bytes()
			test_ReadJWK()
			test_Rng_Guid()
			test_Cipher_Encrypt_Prefix()
			test_X509_MakeCert_EmptyDN()
			test_X509_CertRequest_EmptyDN_extKeyUsage()
			test_X509_ReadCertStringFromPFX_3des()
			test_PFX_MakeFile_3DES()
			test_CMS_MakeSigData_signingcert()
			' Tests from TestPKIcsharpV12.cs 

			test_ReadRSA_ECCKeys()
			test_MakeCerts_ECDSA()
			test_MakeCerts_PSS()
			test_SIG_VerifyData_PSS()
			test_RSA_EncryptDecrypt()
			test_SIG_SignData_PSS()
			test_SIG_SignData_ECDSA()
			test_RSA_ToXMLStringEx()
			test_CMS_MakeSigData_PSS()
			test_MakeSignedEnveloped_PSS_OAEP()
			test_PFX_MakeFile_PSS()
			test_CIPHER_EncryptAEAD()
			test_X509_ReadCertFromP7Chain()
			test_X509_ReadCertFromPFX()
			test_CIPHER_EncryptHex()
			test_RSA_XML_withprefixes()
			test_ECC_DHSharedSecret()
			test_ECC_DHSharedSecret_25519()
			test_SIG_SignData_Ed25519()
			test_X509_MakeCertSelf_Ed25519()
			test_CMS_MakeSigData_Ed25519()
			test_General_FormatErrorMessage()
			test_RSA_SaveEncKey()
			test_ECC_Brainpool()
			test_HASH_Length()
			test_KDF_Bytes()
			test_KDF_ForCms()
			test_CMS_EnvData_ecdh()
			test_X509_MakeCert_Internal_X25519()
			test_PFX_MakeFile_AES256()
			test_CMS_EnvData_auth()
			test_CMS_EnvData_pwri()
			test_CMS_EnvData_kekri()
			test_CMS_EnvData_examples()
			test_Hash_SHA3()
			test_Hmac_SHA3()
			test_Xof()
			test_Prf()
			test_CIPHER_File_GCM()
			test_RSA_ReadPublicKey_CSR()
			test_CNV_ShortNamePath()
			test_X509_MakeCert_Ex()
			test_CNV_ShortNamePath_EncryptFile()
		End Sub


		'********************
		' THE TEST MODULES...
		'********************

		Private Shared Sub test_General()
			Dim n As Integer
			Dim ch As Char
			Dim s As String
			'****************
			' GENERAL TESTS *
			'****************
			Console.WriteLine(vbLf & "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.ModuleInfo()
			Console.WriteLine("ModuleInfo={0}", s)
			s = CryptoSysPKI.General.CompileTime()
			Console.WriteLine("CompileTime={0}", s)
			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)
		End Sub

		Private Shared Sub test_Tdea()
			'*******************************************
			' TDEA (Triple DES, 3DES) ENCRYPTION TESTS *
			'*******************************************

			Dim s As String
			Dim n As Integer
			Dim b As Byte()
			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
			Console.WriteLine(vbLf & "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"
			File.WriteAllText(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 = File.ReadAllBytes(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 = File.ReadAllText(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.")

		End Sub

		Private Shared Sub test_Rsa()
			'************
			' RSA TESTS *
			'************
			Dim s As String
			Dim n As Integer, nblock 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 fnameOutput As String, fname As String
			Dim strCheck As String

			Console.WriteLine(vbLf & "RSA FUNCTION TESTS:")
			' Read the public key from the recipient's X.509 certificate
			sbPublicKey = Rsa.ReadPublicKey("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.ReadPrivateKey("AlicePrivRSASign.p8e", "password")
			Console.WriteLine("PrivateKey={0}", sbPrivateKey.ToString())
			Console.WriteLine("PrivateKeyBits={0}", Rsa.KeyBits(sbPrivateKey.ToString()))
			Console.WriteLine("PrivateKeyBytes={0}", Rsa.KeyBytes(sbPrivateKey.ToString()))

			' Decrypt with private key
			b = Rsa.RawPrivate(b, sbPrivateKey.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())
			' [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())
			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 ByteArraysEqual(bcheck, b) Then
				Console.WriteLine("OK, verification OK")
			Else
				Console.WriteLine("ERROR: verification failed")
			End If
			Debug.Assert(ByteArraysEqual(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(ByteArraysEqual(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(ByteArraysEqual(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(ByteArraysEqual(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())

			' 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.p8e", "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.ReadPrivateKey(fnameOutput, "password")
			strCheck = sbPrivateKey.ToString()
			sbPrivateKey = Rsa.ReadPrivateKey("AlicePrivRSASign.p8e", "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.ReadPrivateKey(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.ReadPrivateKey(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");



			' Show that a private and public key string are matched
			' Read in the private key from the encrypted key file
			sbPrivateKey = Rsa.ReadPrivateKey("AlicePrivRSASign.p8e", "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.ReadPublicKey("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.ReadPublicKey("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")

		End Sub

		Private Shared Sub test_Rsa_XML()
			'*********************
			' RSA TESTS WITH XML *
			'*********************
			Dim s As String
			Dim sbPrivateKey As StringBuilder
			Dim fldName As String
			Dim xmlkey As String

			Console.WriteLine(vbLf & "RSA TESTS WITH XML:")
			' Read in the private key from the encrypted key file
			sbPrivateKey = Rsa.ReadPrivateKey("AlicePrivRSASign.p8e", "password")

			' Export private key to XML format as <RSAKeyPair>
			s = Rsa.ToXMLString(sbPrivateKey.ToString(), 0)
			Console.WriteLine("Private key in XML format:" & vbLf & "{0}", s)

			' Export key to XML format using <RSAKeyValue> instead of correct <RSAKeyPair>
			' and add "ds" prefix.
			s = Rsa.ToXMLString(sbPrivateKey.ToString(), "ds", Rsa.XmlOptions.ForceRSAKeyValue)
			Console.WriteLine("Private key in XML format, .NET style tag name, plus ds: prefix:" & vbLf & "{0}", s)

			' Export just the public key to XML format
			s = Rsa.ToXMLString(sbPrivateKey.ToString(), Rsa.XmlOptions.ExcludePrivateParams)
			Console.WriteLine("Public key in XML format extracted from private key:" & vbLf & "{0}", s)

			' Test re-import from XML (NB just the public here)
			xmlkey = s
			s = Rsa.FromXMLString(xmlkey)
			' NB the internal strings are always different even for the same key
			Console.WriteLine("Internal key (always different):" & vbLf & "{0}", s)
			' Show we can read this new internal (public) key
			Console.WriteLine("Key bits={0}, KeyHashCode=0x{1:X8}", Rsa.KeyBits(s), Rsa.KeyHashCode(s))
			Debug.Assert(Rsa.KeyBits(s) > 0)

			' Try and import a private key from a public XML key (should fail)
			s = Rsa.FromXMLString(xmlkey, Rsa.XmlOptions.RequirePrivate)
			Console.WriteLine("Rsa.FromXMLString(xmlkey, Rsa.XmlOptions.RequirePrivate) returns '{0}' (expecting empty string)", s)
			Console.WriteLine("ErrorCode={0}, LastError()='{1}'", General.ErrorCode(), General.LastError())
			Debug.Assert(s.Length = 0)

			' Use Rsa.KeyValue to get base64 values for XML <RSAKeyValue> node
			Console.WriteLine("Use Rsa.KeyValue()...")
			fldName = "Modulus"
			s = Rsa.KeyValue(sbPrivateKey.ToString(), fldName)
			Console.WriteLine("{0}={1}", fldName, s)
			Debug.Assert(s.Length > 0)
			fldName = "Exponent"
			s = Rsa.KeyValue(sbPrivateKey.ToString(), fldName)
			Console.WriteLine("{0}={1}", fldName, s)
			Debug.Assert(s.Length > 0)

			' Read in a private key when we know only (n,e,d)
			xmlkey = "<RSAKeyPair>" & "<Modulus EncodingType='hexBinary'>E08973398DD8F5F5E88776397F4EB005BB5383DE0FB7ABDC7DC775290D052E6D12DFA68626D4D26FAA5829FC97ECFA82510F3080BEB1509E4644F12CBBD832CFC6686F07D9B060ACBEEE34096A13F5F7050593DF5EBA3556D961FF197FC981E6F86CEA874070EFAC6D2C749F2DFA553AB9997702A648528C4EF357385774575F</Modulus>" & "<Exponent EncodingType='hexBinary'>010001</Exponent>" & "<D EncodingType='hexBinary'>A403C327477634346CA686B57949014B2E8AD2C862B2C7D748096A8B91F736F275D6E8CD15906027314735644D95CD6763CEB49F56AC2F376E1CEE0EBF282DF439906F34D86E085BD5656AD841F313D72D395EFE33CBFF29E4030B3D05A28FB7F18EA27637B07957D32F2BDE8706227D04665EC91BAF8B1AC3EC9144AB7F21</D>" & "</RSAKeyPair>"
			Console.WriteLine("Private key in XML hexbinary format with only (n,e,d) parameters:" & vbLf & "{0}", xmlkey)
			s = Rsa.FromXMLString(xmlkey)
			Debug.Assert(s.Length > 0)
			' Show we can read this new internal key - hashcode should be same as previous
			Console.WriteLine("Key bits={0}, KeyHashCode=0x{1:X8}", Rsa.KeyBits(s), Rsa.KeyHashCode(s))
			Debug.Assert(Rsa.KeyBits(s) > 0)

		End Sub

		Private Shared Sub test_Pfx()
			'***************************
			' PFX/PKCS-12 FILE FUNCTIONS 
			'***************************
			Dim n As Integer
			Dim fname As String
			Dim s As String
			Dim fnameInput As String, fnameCert As String, fnameP7 As String
			Dim password As String

			Console.WriteLine(vbLf & "TEST PFX/PKCS-12 FILE FUNCTIONS:")

			' Create a PKCS-12 file containing Alice's private key
			fname = "alice.pfx"
			n = Pfx.MakeFile(fname, "AliceRSASignByCarl.cer", "AlicePrivRSASign.p8e", "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")

			' Create a PKCS-12 file excluding Alice's private key; i.e. just the cert
			' and with the cert unencrypted (.PlainCert option)
			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")
			' Get type name of ASN.1 object
			s = Asn1.Type(fnameCert)
			Console.WriteLine("{0} is a '{1}'", fnameCert, s)
			' Check it is an X.509 cert by getting its subject
			s = X509.CertSubjectName(fnameCert, "")
			Console.WriteLine("{0} has subject {1}.", fnameCert, s)

			' And again from a PKCS-12 file with just a certificate
			fnameInput = "alice-nokey.pfx"
			fnameCert = "alice_fromPFX-nokey.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)

			' Make a PFX file with three certificates
			fname = "bob-3certsEnc.pfx"
			password = "password"
			n = Pfx.MakeFile(fname, "BobRSASignByCarl.cer;CarlRSASelf.cer;AliceRSASignByCarl.cer", "BobPrivRSAEncrypt.p8e", password, "Bob's friendly 3-cert ID", 0)
			Console.WriteLine("Pfx.MakeFile(3-certs) returns {0} (expected 0)", n)
			Debug.Assert(0 = n, "Failed to create PFX file")
			Console.WriteLine("Created file '{0}'", fname)
			Debug.Assert(Pfx.SignatureIsValid(fname, password))

			' Extract all the certificate from a PKCS-12 PFX file as a PKCS-7 certificate chain file
			fnameInput = "bob-3certsEnc.pfx"
			fnameP7 = "p7ChainfromPFX.p7c"
			n = X509.GetP7ChainFromPFX(fnameP7, fnameInput, "password")
			Console.WriteLine("X509.GetP7ChainFromPFX returns {0} (expected +ve).", n)
			Debug.Assert(n > 0, "Failed to extract p7c from PFX file")
			' Get type name of ASN.1 object
			s = Asn1.Type(fnameP7)
			Console.WriteLine("{0} is a '{1}'", fnameP7, s)
			' How many certs in it?
			'n = X509.GetCertFromP7Chain("", fnameP7, 0);
			' New method in [v12.2]
			n = X509.GetCertCountInP7Chain(fnameP7)
			Console.WriteLine("{0} contains {1} certificates (expected 3)", fnameP7, n)
			Debug.Assert(n = 3, "Wrong number of certs in P7 file")


		End Sub

		Private Shared Sub test_Rsa_Keys()
			'***************************************
			' ADVANCED PRIVATE KEY SAVING OPTIONS 
			' --- OVERLOAD for Rsa.SaveEncPrivateKey
			'***************************************
			Dim n As Integer
			Dim fname As String
			Dim s As String
			Dim sbPrivateKey As StringBuilder
			Dim sbKeyCheck As StringBuilder

			Console.WriteLine(vbLf & "TEST RSA SAVE ENCRYPTED PRIVATE KEY OPTIONS:")
			' Read in a known private key from its encrypted key file
			Console.WriteLine("Read in a private key...")
			sbPrivateKey = Rsa.ReadPrivateKey("AlicePrivRSASign.p8e", "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.ReadPrivateKey(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.ReadPrivateKey(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.ReadPrivateKey(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")

			' Read private key from a string instead of a file
			Console.WriteLine("Reading key from a PEM string...")
			s = "-----BEGIN ENCRYPTED PRIVATE KEY-----" & "MIICojAcBgoqhkiG9w0BDAEDMA4ECHPQz6NdAmoFAgIH0ASCAoBKn9KXr+dm" & "Vtc0ZhEog7t3Prs4rJazwUsXExU78ePLMquxLi/cPmqtyjb472r6XUOa9J/v" & "g2gYHlJ7D7FfAdTdVbHmXWfZzdIqI+AKZmrMoIfSVSSrI8mLDXLDgJVm2Gxa" & "r/YJ154L4fwqWjj0b06v8nTrXTp7G3ZSxjmXc3auf8tS1RatpDuSn027jBGt" & "Pg2CGPjeSomOU7Efd89R+gryW3RfXaMEv1TtGmdS+szxN4TAzgFTzjzE7qJ2" & "+WL09hBRxSyi5JybbxblrO5zDbGJD8rq4kGawWUj4PYDpOkxQYQyK/cALEvv" & "EipLeWvk03CadKER3EcpL7wQT3N5wJGNx7GR3efkO7lO/VfGf6kYFsJ8Qt94" & "vBlgq84abgSD+rlRX03re/NLJQ00Qxl3bDrkSiRoXSfBiOeVzBVTsh03Sj4B" & "V0v2KLENsMXr40rMqTGfKD3V+FyYUehWEkEl3NrIVpBSJir+g4H3tl76SdNe" & "mq/cTtQP+EY8fpC3I46dyDXFat3wQfubw+E5nGfv7xp6vRVRRolpZx7DpuB/" & "z1tzO3uP0vJ0pjATriO/ZAVs6UrXx+DJ6XsfrAVt0jpW5Ngr8rm2EiD3/1T9" & "7q1dELJ7GzCY1dG99XVjt9ZXb7cI8zsPpT/gzQJLfeLe3U5Mdw0hKZLfPCex" & "0urs3ytK0XNu+jZAYeSaysG8/rHJaH74WOgJ8gnSPY4QtWsu6+3qBErS2jbq" & "7E2jRvBKWICVd1yiQCDq/c6s9LeYhNhZsmcWxuX9b4lG9f1LHZy0djhIYi4x" & "IpcEfjkTH+7zUOkMQ+fXZHtSEVFt9L2Ci49jB8YReqbfOuDFzzwsk3xxfL2h" & "ZoRK" & "-----END ENCRYPTED PRIVATE KEY-----"
			sbKeyCheck = Rsa.ReadPrivateKey(s, "password")
			Console.WriteLine("Private key is " & Rsa.KeyBits(sbKeyCheck.ToString()) & " bits long")
			Console.WriteLine("KeyHashCode={0,8:X}", Rsa.KeyHashCode(sbKeyCheck.ToString()))
			Wipe.[String](sbKeyCheck)

		End Sub

		Private Shared Sub test_Rsa_X509_Stronger()
			Dim n As Integer
			Dim fname As String
			Dim i As Integer, k As Integer
			Dim sbPrivateKey As StringBuilder
			Dim sbPublicKey As StringBuilder
			Dim pubkeyFile As String, prikeyFile As String
			Dim s As String
			Dim fnameCert As String
			Dim issuerCert As String
			Dim distname As String
			Dim query As String
			Dim certList As String
			Dim cert1 As String, cert2 As String

			Console.WriteLine(vbLf & "STRONGER RSA KEY ENCRYPTION:")
			' Read in Carl's key
			fname = "CarlPrivRSASign.p8e"
			Console.WriteLine("Reading private key file {0}", fname)
			sbPrivateKey = Rsa.ReadPrivateKey(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.p8e.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.ReadPrivateKey(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)

			Console.WriteLine("GENERATE NEW RSA KEY PAIR WITH STRONGER ENCRYPTION:")
			' 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, "password", 512, pbes := Rsa.PbeOptions.Pbe_Pbkdf2_des_EDE3_CBC, paramString := "count=3000")
			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.ReadPrivateKey(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, "password", 768, Rsa.PublicExponent.Exp_EQ_65537, Rsa.PbeOptions.Pbe_Pbkdf2_aes128_CBC, _
				"count=3000;prf=hmacWithSHA224", 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.ReadPrivateKey(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(vbLf & "STRONGER X.509 SIGNATURE ALGORITHMS:")
			' Create a new self-signed CA certificate for Carol, using PEM-style key we made above
			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", SigAlgorithm.Rsa_Sha384, X509.CertOptions.[Default])
			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.ReadPublicKey("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", SigAlgorithm.Rsa_Sha256, 0)
			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(vbLf & "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 = File.ReadAllText(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 = File.ReadAllText(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)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "countOfRecipientInfos"
			s = Cms.QueryEnvData(fname, query)
			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)
				Console.WriteLine("{0}='{1}'", query, s)
				query = [String].Concat("recipientSerialNumber", "/", i)
				s = Cms.QueryEnvData(fname, query)
				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)
			' (We're just reading in the file to a string so you can see how to read a string directly!)
			Console.WriteLine("Reading file '{0}'", prikeyFile)
			s = File.ReadAllText(prikeyFile)
			' so instead of using the filename, we can read this string instead
			sbPrivateKey = Rsa.ReadPrivateKey(s, "password")
			Debug.Assert(sbPrivateKey.Length > 0, "Rsa.ReadPrivateKey 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())
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed.")
			Console.WriteLine("Msg='{0}'", s)
			' Clean up
			Wipe.[String](sbPrivateKey)

		End Sub


		Private Shared Sub test_X509()
			'*************
			' X509 TESTS *
			'*************
			Dim s As String
			Dim i As Integer, n As Integer, r As Integer
			Dim isok As Boolean
			Dim sbPublicKey As StringBuilder
			Dim fnameCheck As String
			Dim fnameInput As String, fnameCert As String, fname 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 certList As String, certFile As String, csrFile As String
			Dim extns As String, dn As String, password As String
			Dim pubkeyFile As String, prikeyFile As String

			Console.WriteLine(vbLf & "X509 TESTS:")

			' Generate a fresh RSA key which we'll use later
			pubkeyFile = "mykey_pub.bin"
			prikeyFile = "mykey_epk.bin"

			n = Rsa.MakeKeys(pubkeyFile, prikeyFile, "password", 512)
			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")
			' 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", SigAlgorithm.Rsa_Sha1, X509.CertOptions.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.p8e"
			n = X509.MakeCert(fnameCert, issuerCert, pubkeyFile, prikeyFile, 101, 2, _
				"CN=Me;C=US;O=MyOrg", "", 0, "password", SigAlgorithm.Rsa_Md5, X509.CertOptions.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)
			query = "serialNumber"
			s = X509.QueryCert(fnameCert, query)
			Console.WriteLine("X509.QueryCert('{0}') = {1}", query, s)
			s = X509.QueryCert(fnameCert, query, X509.OutputOpts.[Decimal])
			Console.WriteLine("X509.QueryCert('{0}', Decimal) = {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")

			' 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", SigAlgorithm.[Default], _
				0)
			Console.WriteLine("X509.CertRequest returned {0} (expected 0).", n)
			Debug.Assert(0 = n, "X509.CertRequest failed")

			' Make a certificate signing request using our new private key plus extensions
			prikeyFile = "mykey_epk.bin"
			n = X509.CertRequest("myreq-ext.csr", prikeyFile, "CN=myuser;O=Test Org;C=AU;L=Sydney;S=NSW", "rfc822name=fred@example.com;dnsname=fred.example.com;uri=ftp://ftp.example.com/;ipaddress=192.168.15.1", "password", SigAlgorithm.[Default], _
				X509.CsrOptions.FormatBinary)
			Console.WriteLine("X509.CertRequest returned {0} (expected 0).", n)
			If n <> 0 Then
				disp_error(n)
			End If
			Debug.Assert(0 = n, "X509.CertRequest failed")

			' 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);
			' New method in [v12.2]
			n = X509.GetCertCountInP7Chain(fnameInput)
			Console.WriteLine("X509.GetCertCountInP7Chain returns {0} (expected 2).", n)
			Debug.Assert(n > 0, "X509.GetCertCountInP7Chain failed")
			' 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

			' Bob creates a self-signed cert using his Chinese nickname, Ben
			fnameCert = "benChina.cer"
			prikeyFile = "BobPrivRSAEncrypt.p8e"
			' 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", SigAlgorithm.[Default], X509.CertOptions.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
			Console.WriteLine("NB Chinese characters will not display in the console unless the code page is set to Chinese")
			s = X509.CertSubjectName(fnameCert, "")
			Console.WriteLine("Subject's name is '{0}'", s)
			' 2 -- displaying in LDAP default format
			s = X509.QueryCert(fnameCert, "subjectName", X509.OutputOpts.Ldap)
			Console.WriteLine("Subject's name (LDAP) is '{0}'", s)
			Debug.Assert(s.Length > 0, "Failed to read subjectName")
			' 3 -- displaying in Unicode (UTF-16) converted from UTF-8
			s = X509.QueryCert(fnameCert, "subjectName", X509.OutputOpts.Unicode)
			' NB Chinese characters will not display in the console unless the code page is set to Chinese
			' The string `s` contains the valid Chinese characters, they just don't display in the console.
			Console.WriteLine("Check da='大'")
			' (? in console)
			Console.WriteLine("Subject's name (UTF16) is '{0}'", s)
			Debug.Assert(s.Length > 0, "Failed to read subjectName")

			' Ben then creates a certificate for a Mexican user 
			'(using the public key we created above and his chinese certificate. Hey, Silk Road!)
			pubkeyFile = "mykey_pub.bin"
			issuerCert = "benChina.cer"
			fnameCert = "mariaMexico.cer"
			' passing the UTF-8 characters for the name in hex format
			' C=México;CN=María;OU=#xbabe (yes, we want the OU to be "#xbabe": note the single quotes in the distname string)
			distname = "C=#x4de97869636f;CN=#x4d6172ed61;OU='#xbabe'"
			n = X509.MakeCert(fnameCert, issuerCert, pubkeyFile, prikeyFile, 7, 2, _
				distname, "", 0, "password", SigAlgorithm.[Default], X509.CertOptions.UTF8String)
			Console.WriteLine("X509.MakeCertSelf(mexican) returned {0}", n)
			Debug.Assert(n = 0, "Failed to create new certificate")
			Console.WriteLine("FILE: {0}", fnameCert)

			' Now read in the Subject's name using QueryCert
			' 0 -- bytes as found (in this case UTF-8)
			s = X509.QueryCert("mariaMexico.cer", "subjectName")
			Console.WriteLine("Subject's name (default) is '{0}'", s)
			Debug.Assert(s.Length > 0, "Failed to read subjectName")
			' 1 -- forcing the name to be converted back to Latin-1 encoding
			s = X509.QueryCert("mariaMexico.cer", "subjectName", X509.OutputOpts.Latin1)
			Console.WriteLine("Subject's name (Latin-1) is '{0}'", s)
			Debug.Assert(s.Length > 0, "Failed to read subjectName")
			' 2 -- displaying in LDAP default format
			s = X509.QueryCert("mariaMexico.cer", "subjectName", X509.OutputOpts.Ldap)
			Console.WriteLine("Subject's name (LDAP) is '{0}'", s)
			Debug.Assert(s.Length > 0, "Failed to read subjectName")
			' 3 -- LDAP format forcing UTF-8 (prints "funny")  -- [2021-11-12] REMOVED
			' 4 -- LDAP format plus Latin-1
			s = X509.QueryCert("mariaMexico.cer", "subjectName", X509.OutputOpts.Ldap Or X509.OutputOpts.Latin1)
			Console.WriteLine("Subject's name (LDAP+Latin1) is '{0}'", s)
			Debug.Assert(s.Length > 0, "Failed to read subjectName")
			' 5 -- UTF-8 output converted automatically to Unicode
			s = X509.QueryCert("mariaMexico.cer", "subjectName", X509.OutputOpts.Unicode)
			Console.WriteLine("Subject's name (Unicode/UTF16) is '{0}'", s)
			Debug.Assert(s.Length > 0, "Failed to read subjectName")

			' Dump cert info
			Console.WriteLine("TextDumpToString({0}, Latin1):", fnameCert)
			s = X509.TextDumpToString(fnameCert, X509.OutputOpts.Latin1)
			Console.WriteLine(s)
			Debug.Assert(s.Length > 0, "X509.TextDumpToString failed")

			' Dump cert info
			Console.WriteLine("TextDumpToString({0}, UTF16):", fnameCert)
			s = X509.TextDumpToString(fnameCert, X509.OutputOpts.Unicode)
			Console.WriteLine(s)
			Debug.Assert(s.Length > 0, "X509.TextDumpToString failed")
			r = X509.TextDump("mariaMexico.out.UTF8.txt", fnameCert, X509.OutputOpts.Unicode)
			Debug.Assert(r = 0, "X509.TextDump failed")

			' 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 starting with it)
			pubkeyFile = "AlicePubRSA.pub"
			sbPublicKey = Rsa.ReadPublicKey("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.p8e"
			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, SigAlgorithm.[Default], X509.CertOptions.AuthKeyId)
			Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r)
			If r <> 0 Then
				Console.WriteLine(" error=" & General.LastError())
			End If
			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")

			' Create a new certificate using a PKCS-10 certificate signing request (CSR)
			' instead of using a subjectPublicKey file and distinguished name.
			Console.WriteLine("Create a new certificate using a PKCS-10 CSR file...")
			csrFile = "myreq.txt"
			s = X509.TextDumpToString(csrFile, 0)
			Console.WriteLine(s)
			fnameCert = "mycert-fromCSR.cer"
			issuerCert = "CarlRSASelf.cer"
			prikeyFile = "CarlPrivRSASign.p8e"
			password = "password"
			extns = "notBefore=2010-01-01T12:00"
			' Make an end-user cert valid for 24 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, 24, _
				"", extns, 0, password, SigAlgorithm.[Default], 0)
			Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(r = 0, "X509.MakeCert failed")
			Console.WriteLine("Created cert file {0}", fnameCert)
			' Print out a text dump of the certificate
			Console.WriteLine(X509.TextDumpToString(fnameCert, 0))

			' 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")


		End Sub

		Private Shared Sub test_CRL()
			'******************************************
			' CRL (CERTIFICATE REVOCATION LIST) TESTS *
			'******************************************
			Dim r As Integer
			Dim prikeyFile As String
			Dim issuerCert As String
			Dim certList As String, certFile As String, crlFile As String
			Dim extns As String, password As String, dateStr As String

			' [updated in v12.0]
			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.p8e"
			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, "", _
				SigAlgorithm.[Default], 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, _
				SigAlgorithm.Rsa_Sha256, 0)
			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 = X509.Revoked (changed in [v12.0] formerly +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, X509.Revoked)
			Debug.Assert(r = X509.Revoked, "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


		End Sub

		Private Shared Sub test_OCSP()
			'**************************************************
			' OCSP (ONLINE CERTIFICATE STATUS PROTOCOL) TESTS *
			'**************************************************
			Dim s As String
			Dim fname As String
			Dim issuerCert As String
			Dim certFile As String, snStr As String

			Console.WriteLine(vbLf & "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)


		End Sub

		Private Shared Sub test_CMS(doBigFile As Boolean)
			'*****************************
			' CMS (S/MIME OBJECTS) TESTS *
			'*****************************
			Dim s As String
			Dim n As Integer
			Dim b As Byte()
			Dim bcheck As Byte()
			Dim sbPrivateKey As StringBuilder
			Dim fnameInput As String, fnameOutput As String, fnameCert As String, fname As String
			Dim hexDigest As String
			Dim strCheck As String
			Dim query As String
			Dim certList As String
			Dim flen As Long

			Console.WriteLine(vbLf & "CMS (S/MIME OBJECTS) TESTS:")
			' Create a test text file
			fnameInput = "excontent.txt"
			File.WriteAllText(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, CipherAlgorithm.Tdea)
			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.ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password")
			Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key")
			n = Cms.ReadEnvDataToFile(fnameOutput, fnameInput, "", sbPrivateKey.ToString())
			Console.WriteLine("Cms.ReadEnvData returns {0} (expecting 0)", n)
			Debug.Assert(0 = n, "Cms.ReadEnvData failed")
			s = File.ReadAllText(fnameOutput)
			Console.WriteLine("MSG={0}", s)

			' and again but read directly into a string
			s = Cms.ReadEnvDataToString(fnameInput, "", sbPrivateKey.ToString())
			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, CipherAlgorithm.Tdea, Cms.EnvDataOptions.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.ReadOptions.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.ReadPrivateKey("AlicePrivRSASign.p8e", "password")
			fnameOutput = "BasicSignByAlice.bin"
			fnameInput = "excontent.txt"
			fnameCert = "AliceRSASignByCarl.cer"
			n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(), Cms.SigAlg.[Default], 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 = File.ReadAllText(fnameInput)
			fname = "BasicSignByAlice1.bin"
			n = Cms.MakeSigDataFromString(fname, s, fnameCert, sbPrivateKey.ToString(), Cms.SigAlg.[Default], 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 = File.ReadAllBytes(fnameOutput)
			bcheck = File.ReadAllBytes(fname)
			Debug.Assert(ByteArraysEqual(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(), HashAlgorithm.Sha1, 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 = File.ReadAllText(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.SigAlg.[Default], Cms.SigDataOptions.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.SigAlg.[Default], Cms.SigDataOptions.IncludeAttributes Or Cms.SigDataOptions.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
			fnameOutput = "BasicSignByAlice_MD5.txt"
			fnameInput = "excontent.txt"
			fnameCert = "AliceRSASignByCarl.cer"
			' Ver 12.2 prefer explicit signature algorithm
			n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(), Cms.SigAlg.Rsa_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")

			' Prefer explicit SigAlg + Hash option [v12.2]
			' 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(), Cms.SigAlg.Rsa_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 = File.ReadAllText("excontent.txt")
			fname = "BasicSignByAlice_224.bin"
			n = Cms.MakeSigDataFromString(fname, s, fnameCert, sbPrivateKey.ToString(), Cms.SigAlg.Rsa_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.SigAlg.[Default], Cms.SigDataOptions.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.

		End Sub

		Private Shared Sub test_CMS_SigData_PSS()
			Console.WriteLine(vbLf & "CMS SIG-DATA WITH RSA-PSS:")

			' Use German Health examples folder for now
			Const  TESTPATH As String = "C:\Test\GermanHealth\"
			Dim prikeyFile As String = TESTPATH & "999009991b_pri.p8e"
			Dim userCert As String = TESTPATH & "999009991b.cer"
			Dim recipKeyFile As String = TESTPATH & "999009051b_pri.p8e"
			Dim recipCert As String = TESTPATH & "999009051b.cer"
			Dim certList As String = recipCert & ";" & TESTPATH & "CA_4096_Cert.cer" & ";" & TESTPATH & "Int_4096_Cert.cer"
			Dim interFile As String = TESTPATH & "To_999009051b.int"
			Dim outFile As String = TESTPATH & "To_999009051b.p7m"
			Dim interFile1 As String = TESTPATH & "To_999009051b_1.int"
			Dim msg As String = "Hallo Walt"

			Dim sbPrivateKey As StringBuilder
			Dim r As Integer
			Dim s As String
			Dim query As String

			' Read in the sender's private key
			sbPrivateKey = Rsa.ReadPrivateKey(prikeyFile, "password")
			Console.WriteLine("Key length={0} bits", Rsa.KeyBits(sbPrivateKey.ToString()))
			Debug.Assert(sbPrivateKey.Length > 0)

			' Create intermediate signed-data file
			r = Cms.MakeSigDataFromString(interFile, msg, userCert, sbPrivateKey.ToString(), Cms.SigAlg.Rsa_Pss_Sha256, Cms.SigDataOptions.IncludeAttributes Or Cms.SigDataOptions.AddSignTime)
			Wipe.[String](sbPrivateKey)
			Debug.Assert(r = 0)
			Console.WriteLine("Created intermediate signed-data file '{0}'", interFile)
			asn1DumpFile(interFile)

			' While we're here, let's query the signed-data file
			query = "signatureAlgorithm"
			s = Cms.QuerySigData(interFile, query)
			Console.WriteLine("Cms.QuerySigData('{0}')='{1}'", query, s)
			query = "pssParams"
			s = Cms.QuerySigData(interFile, query)
			Console.WriteLine("Cms.QuerySigData('{0}')='{1}'", query, s)

			' Encrypt signed-data directly in enveloped-data file: NB returns number of recipients, not zero on success
			r = Cms.MakeEnvData(outFile, interFile, certList, CipherAlgorithm.Aes256, Cms.KeyEncrAlgorithm.Rsa_Oaep, HashAlgorithm.Sha256, _
				Cms.EnvDataOptions.None)
			Debug.Assert(r > 0)
			Console.WriteLine("Created enveloped-data file '{0}'", outFile)

			' And let's query the enveloped-data file
			query = "keyEncryptionAlgorithm"
			s = Cms.QueryEnvData(outFile, query)
			Console.WriteLine("Cms.QueryEnvData('{0}')='{1}'", query, s)
			query = "oaepParams"
			s = Cms.QueryEnvData(outFile, query)
			Console.WriteLine("Cms.QueryEnvData('{0}')='{1}'", query, s)

			' Clean up
			Wipe.[String](sbPrivateKey)


			Console.WriteLine(vbLf & "Part 2: Reading in the enveloped data...")
			' Read in the recipient's private key (which we shouldn't have, but we do here for testing)
			sbPrivateKey = Rsa.ReadPrivateKey(recipKeyFile, "password")
			Console.WriteLine("Key length={0} bits", Rsa.KeyBits(sbPrivateKey.ToString()))
			Debug.Assert(sbPrivateKey.Length > 0)

			' Read the enveloped-data file to output the signed-data file
			r = Cms.ReadEnvDataToFile(interFile1, outFile, recipCert, sbPrivateKey.ToString())
			Debug.Assert(r = 0)
			Console.WriteLine("Read in signed-data file '{0}'", interFile1)

			' Verify the signed data before reading it
			r = Cms.VerifySigData(interFile1)
			Console.WriteLine("Cms.VerifySigData() returns {0} (expecting 0)", r)
			Debug.Assert(r = 0)

			' Read in the data that was signed
			s = Cms.ReadSigDataToString(interFile1)
			Console.WriteLine("msg='{0}'", s)
			Debug.Assert(s.Length > 0)

			' Clean up
			Wipe.[String](sbPrivateKey)

			' Some more tests...
			Console.WriteLine("Dump certificate data...")
			Console.WriteLine([String].Empty.PadLeft(20, "="C))
			s = X509.TextDumpToString(recipCert, X509.OutputOpts.[Decimal] Or X509.OutputOpts.Ldap)
			Debug.Assert(s.Length > 0)
			Console.WriteLine(s)
			Console.WriteLine([String].Empty.PadLeft(20, "="C))
			s = Asn1.TextDumpToString(recipCert, 0)
			Debug.Assert(s.Length > 0)
			Console.WriteLine(s)

			r = X509.ValidatePath(certList)
			Console.WriteLine("X509.ValidatePath() returns {0} (expecting 0)", r)
			Debug.Assert(r = 0)

		End Sub

		Private Shared Sub test_CMS_EnvData()
			Dim s As String
			Dim n As Integer
			Dim fname As String
			Dim sbPrivateKey As StringBuilder
			Dim query As String
			Dim fnameCert As String

			Console.WriteLine(vbLf & "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)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "contentEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)

			' Now try and read it using Bob's private key
			sbPrivateKey = Rsa.ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password")
			Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key")
			s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString())
			Console.WriteLine("MSG={0}", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

		End Sub

		Private Shared Sub test_CMS_EnvData_ecdh()
			Dim s As String
			Dim n As Integer
			Dim fname As String
			Dim sbPrivateKeyAlice As StringBuilder, sbPrivateKeyDana As StringBuilder
			Dim query As String
			Dim certName As String, certList As String
			Dim nRecips As Integer

			Console.WriteLine(vbLf & "ENVELOPED-DATA OBJECTS USING ECDH KEY AGREEMENT (KARI):")
			' Create an enveloped CMS object to Dana (using ECDH) and Alice (using RSA)
			fname = "dana_alice_all_defaults.p7m"
			certList = "lamps-dana.encrypt.crt;lamps-alice.encrypt.crt"
			s = "This is some sample content."
			' This should return 2 (indicating two successful recipients)
			nRecips = 2
			' Use defaults for ECDH
			n = Cms.MakeEnvDataFromString(fname, s, certList, CipherAlgorithm.Aes128)
			Console.WriteLine("Cms.MakeEnvDataFromString returns {0} (expecting {1})", n, nRecips)
			If nRecips <> n Then
				Console.WriteLine(General.FormatErrorMessage(n))
			End If
			Debug.Assert(nRecips = n, "Cms.MakeEnvDataFromString failed")

			Console.WriteLine("FILE: {0}", fname)
			'Console.WriteLine(Asn1.TextDumpToString(fname));
			'
'            openssl cms -decrypt -in dana_alice_all_defaults -inform DER -inkey lamps-alice.decrypt.p8.pem -recip lamps-alice.encrypt.crt
'            This is some sample content.
'            


			' Query the enveloped-data file
			query = "contentEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "recipientInfoType"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "keyEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "keyEncryptionAlgorithm/2"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)

			fname = "dana_alice_oaep_ecc_defaults.p7m"
			certList = "lamps-dana.encrypt.crt;lamps-alice.encrypt.crt"
			s = "This is some sample content."
			' This should return 2 (indicating two successful recipients)
			nRecips = 2
			' Use RSA-OAEP but defaults for ECDH
			n = Cms.MakeEnvDataFromString(fname, s, certList, CipherAlgorithm.Aes128, Cms.KeyEncrAlgorithm.Rsa_Oaep, HashAlgorithm.Sha256, _
				Cms.EnvDataOptions.Mgf1Sha1)
			Console.WriteLine("Cms.MakeEnvDataFromString returns {0} (expecting {1})", n, nRecips)
			If nRecips <> n Then
				Console.WriteLine(General.FormatErrorMessage(n))
			End If
			Debug.Assert(nRecips = n, "Cms.MakeEnvDataFromString failed")

			Console.WriteLine("FILE: {0}", fname)

			' Query the enveloped-data file
			query = "contentEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "recipientInfoType"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "keyEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "keyEncryptionAlgorithm/2"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)

			' Read in Alice's RSA and Dana's ECC private key (unencrypted)
			Console.WriteLine("Read in Alice's and Dana's private keys")
			sbPrivateKeyAlice = Rsa.ReadPrivateKey("lamps-alice.decrypt.p8.pem", "")
			Debug.Assert(sbPrivateKeyAlice.ToString().Length > 0, "Unable to read Alices's private key")
			sbPrivateKeyDana = Ecc.ReadPrivateKey("lamps-dana.decrypt.p8.pem", "")
			Debug.Assert(sbPrivateKeyDana.ToString().Length > 0, "Unable to read Dana's private key")

			' Read CMS enveloped-data
			Console.WriteLine("Read CMS using Alice's private key")
			certName = "lamps-alice.encrypt.crt"
			s = Cms.ReadEnvDataToString(fname, certName, sbPrivateKeyAlice.ToString())
			Console.WriteLine("MSG={0}", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

			' And again using Dana's X25519 key
			Console.WriteLine("Read using Dana's ECC X22519 private key")
			certName = "lamps-dana.encrypt.crt"
			s = Cms.ReadEnvDataToString(fname, certName, sbPrivateKeyDana.ToString())
			Console.WriteLine("MSG={0}", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

			' Now use specific ECDH parameters added [v20.5]
			fname = "dana_alice_hkdf.p7m"
			certList = "lamps-dana.encrypt.crt"
			nRecips = 1
			n = Cms.MakeEnvData(fname, "excontent.txt", certList, CipherAlgorithm.Aes256, 0, HashAlgorithm.Sha256, _
				0, Kdf.KdfAlg.Hkdf, Kdf.KeyWrapAlg.Aes256_wrap)
			Console.WriteLine("Cms.MakeEnvData returns {0} (expecting {1})", n, nRecips)
			If nRecips <> n Then
				Console.WriteLine(General.FormatErrorMessage(n))
			End If
			Debug.Assert(nRecips = n, "Cms.MakeEnvDataFromString failed")

			Console.WriteLine("FILE: {0}", fname)
			'Console.WriteLine(Asn1.TextDumpToString(fname));

			' Query the enveloped-data file
			query = "contentEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "keyEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "keyWrapAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)

			certName = "lamps-dana.encrypt.crt"
			s = Cms.ReadEnvDataToString(fname, certName, sbPrivateKeyDana.ToString())
			Console.WriteLine("MSG={0}", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

			Console.WriteLine("EXPECTING ERRORS...")
			fname = "dana-bad.p7m"
			certList = "lamps-dana.encrypt.crt"
			nRecips = 1
			' Pass bad parameters: MD5 is not valid here for ecdhX963KDF
			n = Cms.MakeEnvData(fname, "excontent.txt", certList, CipherAlgorithm.Aes256, 0, HashAlgorithm.Md5)
			Console.WriteLine("Cms.MakeEnvData returns {0} (expecting {1})", n, nRecips)
			If nRecips <> n Then
				Console.WriteLine(General.FormatErrorMessage(n))
			End If
			Debug.Assert(nRecips <> n, "Cms.MakeEnvDataFromString succeeded when should fail")
			' Cannot use cms3DESwrap when content encryption is not Triple DES
			n = Cms.MakeEnvData(fname, "excontent.txt", certList, CipherAlgorithm.Aes256, keyWrapAlg := Kdf.KeyWrapAlg.Cms3DESwrap)
			Console.WriteLine("Cms.MakeEnvData returns {0} (expecting {1})", n, nRecips)
			If nRecips <> n Then
				Console.WriteLine(General.FormatErrorMessage(n))
			End If
			Debug.Assert(nRecips <> n, "Cms.MakeEnvDataFromString succeeded when should fail")

			Console.WriteLine("...END OF EXPECTED ERRORS")

		End Sub

		Private Shared Sub test_CMS_EnvData_auth()
			Dim s As String
			Dim n As Integer
			Dim fname As String
			Dim sbPrivateKey As StringBuilder
			Dim query As String
			Dim fnameCert As String

			Console.WriteLine(vbLf & "AUTHENTICATED-ENVELOPED-DATA OBJECTS:")
			' Create an authenticated-enveloped CMS object Alice to Bob using Bob's X.509 certificate
			fname = "cms2bob_auth.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, _
				Cms.EnvDataOptions.Authenticated, count := 13)
			Console.WriteLine("Cms.MakeEnvDataFromString returns {0} (expecting 1)", n)
			If n < 0 Then
				Console.WriteLine(General.FormatErrorMessage(n))
			End If
			Debug.Assert(1 = n, "Cms.MakeEnvDataFromString failed")

			' Query the enveloped-data file
			query = "keyEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "contentEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)

			' Display ASN.1 data
			Console.WriteLine(Asn1.TextDumpToString(fname))

			' Now try and read it using Bob's private key
			sbPrivateKey = Rsa.ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password")
			Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key")
			s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString())
			Console.WriteLine("MSG='{0}'", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")


		End Sub

		Private Shared Sub test_CMS_EnvData_pwri()
			Dim s As String
			Dim n As Integer
			Dim fname As String
			Dim query As String
			Dim sbPassword As New StringBuilder("password1234")

			Console.WriteLine(vbLf & "ENVELOPED-DATA USING PWRI:")
			fname = "cms_envdata_pwri.p7m"
			' This should return 1 (indicating one successful recipient)
			s = "This is some sample content."
			n = Cms.MakeEnvDataFromString(fname, s, "type=@pwri", CipherAlgorithm.Aes128, keyString := sbPassword.ToString())
			Console.WriteLine("Cms.MakeEnvDataFromString(pwri) returns {0} (expecting 1)", n)
			If n < 0 Then
				Console.WriteLine(General.FormatErrorMessage(n))
			End If
			Debug.Assert(1 = n, "Cms.MakeEnvDataFromString(pwri) failed")

			' Query the enveloped-data file
			query = "keyEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "contentEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)

			' Display ASN.1 data
			Console.WriteLine(Asn1.TextDumpToString(fname))

			' Now try and read it using the same password
			s = Cms.ReadEnvDataToString(fname, "", sbPassword.ToString())
			Console.WriteLine("MSG='{0}'", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

			' Clear password
			Wipe.[String](sbPassword)

		End Sub

		Private Shared Sub test_CMS_EnvData_kekri()
			Dim s As String
			Dim n As Integer
			Dim fname As String
			Dim query As String
			Dim kekstr As String = "#x0123456789ABCDEFF0E1D2C3B4A59687"
			' Represents KEK of 16 bytes/128 bits suitable for aes128-wrap
			Console.WriteLine(vbLf & "ENVELOPED-DATA USING KEKRI:")
			fname = "cms_envdata_kekri.p7m"
			' This should return 1 (indicating one successful recipient)
			s = "This is some sample content."
			n = Cms.MakeEnvDataFromString(fname, s, "type=@kekri,keyid=ourcommonkey", CipherAlgorithm.Aes192, hashAlg := HashAlgorithm.Sha256, keyWrapAlg := Kdf.KeyWrapAlg.Aes128_wrap, _
				keyString := kekstr)
			Console.WriteLine("Cms.MakeEnvDataFromString(kekri) returns {0} (expecting 1)", n)
			If n < 0 Then
				Console.WriteLine(General.FormatErrorMessage(n))
			End If
			Debug.Assert(1 = n, "Cms.MakeEnvDataFromString(kekri) failed")

			' Query the enveloped-data file
			query = "keyEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)
			query = "contentEncryptionAlgorithm"
			s = Cms.QueryEnvData(fname, query)
			Console.WriteLine("{0}='{1}'", query, s)

			' Display ASN.1 data
			Console.WriteLine(Asn1.TextDumpToString(fname))

			' Now try and read it using the same KEK
			s = Cms.ReadEnvDataToString(fname, "", kekstr)
			Console.WriteLine("MSG='{0}'", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

		End Sub

		Private Shared Sub test_CMS_EnvData_examples()
			Dim n As Integer

			Console.WriteLine(vbLf & "ENVELOPED-DATA EXAMPLES USED IN DOCS:")

			' Create an enveloped CMS object (ktri type) to Bob using Bob's RSA key
			n = Cms.MakeEnvData("cms2bob_aes128.p7m", "excontent.txt", "BobRSASignByCarl.cer", CipherAlgorithm.Aes128, Cms.KeyEncrAlgorithm.Rsa_Oaep)
			Console.WriteLine("Cms.MakeEnvData returns {0} (expecting 1)", n)
			Debug.Assert(1 = n, "Cms.MakeEnvData failed")

			' Same but using authenticated encryption and creating an authEnvelopedData object
			n = Cms.MakeEnvData("cms2bob_aes128auth.p7m", "excontent.txt", "BobRSASignByCarl.cer", CipherAlgorithm.Aes128, Cms.KeyEncrAlgorithm.Rsa_Oaep, advOptions := Cms.EnvDataOptions.Authenticated)
			Console.WriteLine("Cms.MakeEnvData returns {0} (expecting 1)", n)
			Debug.Assert(1 = n, "Cms.MakeEnvData failed")

			' Create an enveloped CMS object (kari type) to Dana using Dana's ECC key
			n = Cms.MakeEnvData("cms2dana_hkdf.p7m", "excontent.txt", "lamps-dana.encrypt.crt", CipherAlgorithm.Aes256, 0, HashAlgorithm.Sha256, _
				0, Kdf.KdfAlg.Hkdf, Kdf.KeyWrapAlg.Aes256_wrap)
			Console.WriteLine("Cms.MakeEnvData returns {0} (expecting 1)", n)
			Debug.Assert(1 = n, "Cms.MakeEnvData failed")

			' Create an enveloped CMS object (kekri type) using a previously distributed symmetric key-encryption key (KEK) 
			n = Cms.MakeEnvData("cms_envdata_kekri.p7m", "excontent.txt", "type=@kekri,keyid=ourcommonkey", CipherAlgorithm.Aes256, hashAlg := HashAlgorithm.Sha256, keyWrapAlg := Kdf.KeyWrapAlg.Aes128_wrap, _
				keyString := "#x0123456789ABCDEFF0E1D2C3B4A59687")
			Console.WriteLine("Cms.MakeEnvData returns {0} (expecting 1)", n)
			Debug.Assert(1 = n, "Cms.MakeEnvData failed")

			' Create an enveloped CMS object (pwri type) using password-based key management 
			n = Cms.MakeEnvData("cms_envdata_pwri.p7m", "excontent.txt", "type=@pwri", CipherAlgorithm.Aes192, keyString := "password12345")
			Console.WriteLine("Cms.MakeEnvData returns {0} (expecting 1)", n)
			Debug.Assert(1 = n, "Cms.MakeEnvData failed")

			' Now read in the enveloped-data objects we made above
			Dim s As String
			Dim fname As String
			Dim sbPrivateKey As StringBuilder

			sbPrivateKey = Rsa.ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password")
			Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key")
			fname = "cms2bob_aes128.p7m"
			Console.WriteLine("{0}=>{1}", fname, Cms.QueryEnvData(fname, "recipientInfoType"))
			s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString())
			Console.WriteLine("MSG='{0}'", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

			fname = "cms2bob_aes128auth.p7m"
			Console.WriteLine("{0}=>{1}", fname, Cms.QueryEnvData(fname, "recipientInfoType"))
			s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString())
			Console.WriteLine("MSG='{0}'", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

			sbPrivateKey = Ecc.ReadPrivateKey("lamps-dana.decrypt.p8.pem", "")
			Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Dana's private key")
			fname = "cms2dana_hkdf.p7m"
			Console.WriteLine("{0}=>{1}", fname, Cms.QueryEnvData(fname, "recipientInfoType"))
			s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString())
			Console.WriteLine("MSG='{0}'", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

			fname = "cms_envdata_kekri.p7m"
			Console.WriteLine("{0}=>{1}", fname, Cms.QueryEnvData(fname, "recipientInfoType"))
			s = Cms.ReadEnvDataToString(fname, "", "#x0123456789ABCDEFF0E1D2C3B4A59687")
			Console.WriteLine("MSG='{0}'", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

			fname = "cms_envdata_pwri.p7m"
			Console.WriteLine("{0}=>{1}", fname, Cms.QueryEnvData(fname, "recipientInfoType"))
			s = Cms.ReadEnvDataToString(fname, "", "password12345")
			Console.WriteLine("MSG='{0}'", s)
			Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed")

		End Sub



		Private Shared Sub test_CMS_SMIME_Chain()
			Dim r As Integer
			Dim s As String
			Dim inpFile As String
			Dim outFile As String
			Dim origFile As String = "sonnets.txt"
			Dim alicekeyfile As String = "AlicePrivRSASign.p8e"
			Dim alicecerfile As String = "AliceRSASignByCarl.cer"
			Dim password As String = "password"
			Dim bobkeyfile As String = "BobPrivRSAEncrypt.p8e"
			Dim bobcerfile As String = "BobRSASignByCarl.cer"
			Dim sbPrikey As StringBuilder
			Dim query As String
			Dim digest1 As String, digest2 As String

			Console.WriteLine(vbLf & "CREATE A CHAIN OF CMS OBJECTS WRAPPED IN S/MIME:")

			' Start with original input file
			inpFile = origFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' Create a CMS compressed-data object
			outFile = "sonnet-compr.p7z"
			r = Cms.MakeComprData(outFile, inpFile)
			Console.WriteLine("Cms.MakeComprData returns {0} (expected 0)", r)
			Debug.Assert(0 = r)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' Wrap in S/MIME
			outFile = "sonnet-compr-smime.txt"
			r = Smime.Wrap(outFile, inpFile, 0)
			Console.WriteLine("Smime.Wrap returns {0} (expected +ve)", r)
			Debug.Assert(r > 0)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' Create a CMS signed-data object signed by Alice
			outFile = "sonnet-sig-compr.p7m"
			sbPrikey = Rsa.ReadPrivateKey(alicekeyfile, password)
			Debug.Assert(sbPrikey.Length > 0)
			r = Cms.MakeSigData(outFile, inpFile, alicecerfile, sbPrikey.ToString(), Cms.SigAlg.[Default], 0)
			Console.WriteLine("Cms.MakeSigData returns {0} (expected 0)", r)
			Debug.Assert(0 = r)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' Wrap in S/MIME
			outFile = "sonnet-sig-compr-smime.txt"
			r = Smime.Wrap(outFile, inpFile, 0)
			Console.WriteLine("Smime.Wrap returns {0} (expected +ve)", r)
			Debug.Assert(r > 0)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' Create a CMS enveloped-data object encrypted for Bob
			outFile = "sonnet-env-sig-compr.p7m"
			r = Cms.MakeEnvData(outFile, inpFile, bobcerfile, CipherAlgorithm.Tdea)
			Console.WriteLine("Cms.MakeEnvData returns {0} (expected 1)", r)
			Debug.Assert(1 = r)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' Wrap in S/MIME
			outFile = "sonnet-env-sig-compr-smime.txt"
			r = Smime.Wrap(outFile, inpFile, 0)
			Console.WriteLine("Smime.Wrap returns {0} (expected +ve)", r)
			Debug.Assert(r > 0)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' NOW REVERSE THE PROCEDURE...
			Console.WriteLine(vbLf & "Reverse the procedure (and check file types as we go)...")

			' What S/MIME type are we starting with?
			query = "smime-type"
			s = Smime.Query(inpFile, query)
			Console.WriteLine("{0} is '{1}'", query, s)

			' Extract from S/MIME
			outFile = "sonnet-out-env-sig-compr.p7m"
			r = Smime.Extract(outFile, inpFile, 0)
			Console.WriteLine("Smime.Extract returns {0} (expected +ve)", r)
			Debug.Assert(r > 0)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' What ASN.1 type did we get?
			s = Asn1.Type(inpFile)
			Console.WriteLine("Found a '{0}' file", s)

			' Bob decrypts enveloped-file using his private key
			outFile = "sonnet-out-sig-compr-smime.txt"
			sbPrikey = Rsa.ReadPrivateKey(bobkeyfile, password)
			Debug.Assert(sbPrikey.Length > 0)
			r = Cms.ReadEnvDataToFile(outFile, inpFile, "", sbPrikey.ToString())
			Console.WriteLine("Cms.ReadEnvDataToFile returns {0} (expected 0)", r)
			Debug.Assert(r = 0)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' What S/MIME type was it?
			query = "smime-type"
			s = Smime.Query(inpFile, query)
			Console.WriteLine("{0} is '{1}'", query, s)

			' Extract from S/MIME
			outFile = "sonnet-out-sig-compr.p7m"
			r = Smime.Extract(outFile, inpFile, 0)
			Console.WriteLine("Smime.Extract returns {0} (expected +ve)", r)
			Debug.Assert(r > 0)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' What ASN.1 type did we get?
			s = Asn1.Type(inpFile)
			Console.WriteLine("Found a '{0}' file", s)
			Debug.Assert(s.Length > 0)

			' Verify the signature in the signed-data object using its embedded certificate
			r = Cms.VerifySigData(inpFile)
			Console.WriteLine("Cms.VerifySigData returns {0} (0=>signature OK)", r)
			Debug.Assert(r = 0)

			' Extract contents of signed-data
			outFile = "sonnet-out-compr-smime.txt"
			r = Cms.ReadSigDataToFile(outFile, inpFile)
			Console.WriteLine("Cms.ReadSigDataToFile returns {0} (expected +ve)", r)
			Debug.Assert(r > 0)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' What S/MIME type was it?
			query = "smime-type"
			s = Smime.Query(inpFile, query)
			Console.WriteLine("{0} is '{1}'", query, s)

			' Extract from S/MIME
			outFile = "sonnet-out-compr.p7z"
			r = Smime.Extract(outFile, inpFile, 0)
			Console.WriteLine("Smime.Extract returns {0} (expected +ve)", r)
			Debug.Assert(r > 0)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' What ASN.1 type did we get?
			s = Asn1.Type(inpFile)
			Console.WriteLine("Found a '{0}' file", s)
			Debug.Assert(s.Length > 0)

			' Finally, read contents of CMS compressed-data object
			outFile = "sonnet-out.txt"
			r = Cms.ReadComprData(outFile, inpFile, 0)
			Console.WriteLine("Cms.ReadComprData returns {0} (expected +ve)", r)
			Debug.Assert(r > 0)
			inpFile = outFile
			Console.WriteLine("File '{0}' is {1} bytes", inpFile, FileLength(inpFile))

			' Check we have no more S/MIME wrapping
			query = "smime-type"
			s = Smime.Query(inpFile, query)
			Console.WriteLine("{0} is '{1}'", query, s)

			' Did we get the same as we started? 
			Console.WriteLine("Compute SHA-1 digests and compare...")
			digest1 = Hash.HexFromFile(outFile, HashAlgorithm.Sha1)
			Console.WriteLine("SHA1('{0}')=" & vbTab & "{1}", outFile, digest1)
			digest2 = Hash.HexFromFile(origFile, HashAlgorithm.Sha1)
			Console.WriteLine("SHA1('{0}')=" & vbTab & "{1}", origFile, digest2)
			Debug.Assert(digest1 = digest2, "Digests do not match")


		End Sub

		Private Shared Sub test_Hash()
			'*************
			' HASH TESTS *
			'*************
			Dim s As String
			Dim fname As String
			Dim strCheck As String
			Dim b As Byte()

			Console.WriteLine(vbLf & "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
			fname = "hello.txt"
			File.WriteAllText(fname, "hello world" & vbCr & vbLf)
			' get digest from binary file
			s = Hash.HexFromFile(fname, 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(fname, HashAlgorithm.Sha1)
			Console.WriteLine("SHA1('hello world+LF')=   {0}", s)

			' Hash of "abc" with input in hex format using SHA-224
			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")

			' New in [v11.0]
			s = Hash.HexFromString("abc", HashAlgorithm.Ripemd160)
			Console.WriteLine("RIPEMD160('abc') =" & s)
			Debug.Assert([String].Compare(s, "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", True) = 0, "RIPEMD160('abc') failed")
			s = Hash.HexFromString("abc", HashAlgorithm.Bitcoin160)
			Console.WriteLine("BITCOIN160('abc')=" & s)
			Debug.Assert([String].Compare(s, "bb1be98c142444d7a56aa3981c3942a978e4dc33", True) = 0, "BITCOIN160('abc') failed")
			b = Hash.[Double](Cnv.FromHex("616263"), HashAlgorithm.Sha256)
			Console.WriteLine("SHA256(SHA256('abc'))=" & vbLf & Cnv.ToHex(b))
			Debug.Assert([String].Compare(Cnv.ToHex(b), "4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358", True) = 0, "SHA256(SHA256('abc')) failed")
		End Sub

		Private Shared Sub test_Hash_SHA3()
			'*************
			' SHA-3 HASH TESTS *
			'*************
			Dim s As String
			Dim fname As String
			Dim b As Byte()

			Console.WriteLine(vbLf & "SHA-3 HASH DIGEST TESTS:")
			s = Hash.HexFromString("abc", HashAlgorithm.Sha3_224)
			Console.WriteLine("SHA-3-224('abc'):" & vbLf & "{0}", s)
			Debug.Assert([String].Compare(s, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", True) = 0, "SHA-224('abc') failed")
			s = Hash.HexFromString("abc", HashAlgorithm.Sha3_256)
			Console.WriteLine("SHA-3-256('abc'):" & vbLf & "{0}", s)
			Debug.Assert([String].Compare(s, "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", True) = 0, "SHA-256('abc') failed")
			s = Hash.HexFromString("abc", HashAlgorithm.Sha3_384)
			Console.WriteLine("SHA-3-384('abc'):" & vbLf & "{0}", s)
			Debug.Assert([String].Compare(s, "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", True) = 0, "SHA-384('abc') failed")
			s = Hash.HexFromString("abc", HashAlgorithm.Sha3_512)
			Console.WriteLine("SHA-3-512('abc'):" & vbLf & "{0}", s)
			Debug.Assert([String].Compare(s, "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", True) = 0, "SHA-512('abc') failed")
			' Using bytes
			b = Hash.BytesFromBytes(Cnv.FromHex("616263"), HashAlgorithm.Sha3_256)
			Console.WriteLine("SHA3-256(b'abc')=" & vbLf & Cnv.ToHex(b))
			' Create a test file
			fname = "abc.txt"
			File.WriteAllText(fname, "abc")
			' Compute digest from binary file
			s = Hash.HexFromFile(fname, HashAlgorithm.Sha3_256)
			Console.WriteLine("SHA-3-256(FILE('abc'))={0}", s)
			Debug.Assert([String].Compare(s, "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", True) = 0, "SHA-256('abc') failed")
			b = Hash.BytesFromFile(fname, HashAlgorithm.Sha3_512)
			Console.WriteLine("SHA-3-512(FILE('abc'))={0}", Cnv.ToHex(b))
			'Debug.Assert(String.Compare(Cnv.ToHex(b), "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", true) == 0, "SHA-256('abc') failed");

			b = Hash.[Double](Cnv.FromHex("616263"), HashAlgorithm.Sha3_256)
			Console.WriteLine("SHA3-256(SHA3-256('abc'))=" & vbLf & Cnv.ToHex(b))
			'Debug.Assert(String.Compare(Cnv.ToHex(b), "4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358", true) == 0, "BITCOIN160('abc') failed");
			b = Hash.[Double](Cnv.FromHex("616263"), HashAlgorithm.Sha3_224)
			Console.WriteLine("SHA3-224(SHA3-224('abc'))=" & vbLf & Cnv.ToHex(b))
		End Sub

		Private Shared Sub test_Hmac()
			'*************
			' HMAC TESTS *
			'*************
			Dim s As String
			Dim i As Integer
			Dim b As Byte()
			Dim key As Byte()
			Dim msg As Byte()
			Dim strCheck As String

			Console.WriteLine(vbLf & "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
			key = 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, key, 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, key, 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...")
			key = New Byte(24) {}
			For i = 0 To 24
				key(i) = CByte(i + 1)
			Next
			Console.WriteLine("Key={0}", Cnv.ToHex(key))
			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, key, 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, key, 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")

			' 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")

		End Sub

		Private Shared Sub test_Hmac_SHA3()
			'*************
			' HMAC-SHA3 TESTS *
			'*************
			Dim s As String
			Dim b As Byte()
			Dim key As Byte()
			Dim msg As Byte()
			Dim strCheck As String

			Console.WriteLine(vbLf & "HMAC-SHA-3 TESTS:")
			Console.WriteLine("NIST HMAC_SHA3-256.pdf Sample #1")
			key = Cnv.FromHex("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")
			msg = System.Text.Encoding.[Default].GetBytes("Sample message for keylen<blocklen")
			strCheck = "4fe8e202c4f058e8dddc23d8c34e467343e23555e24fc2f025d598f558f67205"
			b = Hmac.BytesFromBytes(msg, key, HashAlgorithm.Sha3_256)
			Console.WriteLine("HMAC-SHA-3-256: {0}", Cnv.ToHex(b))
			Console.WriteLine("Correct:        {0}", strCheck)
			Debug.Assert([String].Compare(Cnv.ToHex(b), strCheck, True) = 0, "HMAC-SHA3-256('Sample #1) failed")

			Console.WriteLine("NIST HMAC_SHA3-512.pdf Sample #3")
			key = Cnv.FromHex("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" & vbCr & vbLf & "                202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F" & vbCr & vbLf & "                404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F" & vbCr & vbLf & "                606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F" & vbCr & vbLf & "                8081828384858687")
			msg = System.Text.Encoding.[Default].GetBytes("Sample message for keylen>blocklen")
			strCheck = "5f464f5e5b7848e3885e49b2c385f0694985d0e38966242dc4a5fe3fea4b37d46b65ceced5dcf59438dd840bab22269f0ba7febdb9fcf74602a35666b2a32915"
			s = Hmac.HexFromBytes(msg, key, HashAlgorithm.Sha3_512)
			Console.WriteLine("HMAC-SHA-3-512: " & vbLf & "{0}", s)
			Console.WriteLine("Correct:        " & vbLf & "{0}", strCheck)
			Debug.Assert([String].Compare(s, strCheck, True) = 0, "HMAC-SHA3-512('Sample #3) failed")
		End Sub

		Private Shared Sub test_Wipe()
			'******************
			' WIPE DATA TESTS *
			'******************
			Dim s As String
			Dim b As Byte()
			Dim isok As Boolean
			Dim fname As String
			Dim sbPrivateKey As StringBuilder

			Console.WriteLine(vbLf & "WIPE DATA TESTS:")
			fname = "ImportantSecret.txt"
			File.WriteAllText(fname, "Very important secrets here.")
			s = File.ReadAllText(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))

			sbPrivateKey = Rsa.ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password")
			Console.WriteLine("Before Wipe.String, sbPrivateKey contains {0} characters.", sbPrivateKey.Length)
			Wipe.[String](sbPrivateKey)
			Console.WriteLine("After Wipe.String, sbPrivateKey = [{0}]", sbPrivateKey.ToString())


		End Sub

		Private Shared Sub test_Prompt(doPrompt As Boolean)
			'************************
			' PASSWORD PROMPT TESTS *
			'************************
			Dim s As String
			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
		End Sub

		Private Shared Sub test_Rng(doPrompt As Boolean)
			Dim s As String
			Dim i As Integer, n As Integer
			Dim b As Byte()
			Dim fname As String
			Dim isok As Boolean

			'************
			' RNG TESTS *
			'************
			Console.WriteLine(vbLf & "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) {}
					File.WriteAllBytes(fname, b)
				End If
			End If
			isok = Rng.Initialize(fname)
			Console.WriteLine("Rng.Initialize('{0}') returns {1} (expecting True)", 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 True)", 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")


		End Sub
		'****************************************
		' GENERIC BLOCK CIPHER ENCRYPTION TESTS *
		'****************************************

		Private Shared Sub test_Cipher()
			Dim b As Byte()
			Dim n As Integer
			Dim key As Byte(), plain As Byte(), cipher__1 As Byte(), iv As Byte()

			Console.WriteLine(vbLf & "TESTING CIPHER(tdea, bytes):")
			' Encrypt in CBC mode using byte arrays
			key = Cnv.FromHex("737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32")
			iv = Cnv.FromHex("B36B6BFB6231084E")
			plain = Cnv.FromHex("5468697320736F6D652073616D706520636F6E74656E742E0808080808080808")
			cipher__1 = Cnv.FromHex("d76fd1178fbd02f84231f5c1d2a2f74a4159482964f675248254223daf9af8e4")
			b = Cipher.Encrypt(plain, key, iv, CipherAlgorithm.Tdea, Mode.CBC)
			Console.WriteLine("KY={0}", Cnv.ToHex(key))
			Console.WriteLine("IV={0}", Cnv.ToHex(iv))
			Console.WriteLine("PT={0}", Cnv.ToHex(plain))
			Console.WriteLine("CT={0}", Cnv.ToHex(b))
			Console.WriteLine("OK={0}", Cnv.ToHex(cipher__1))
			Debug.Assert(ByteArraysEqual(b, cipher__1), "Cipher.Encrypt{bytes,tdea-CBC} failed")
			' Decrypt
			b = Cipher.Decrypt(cipher__1, key, iv, CipherAlgorithm.Tdea, Mode.CBC)
			Console.WriteLine("P'={0}", Cnv.ToHex(b))
			Console.WriteLine("OK={0}", Cnv.ToHex(plain))
			Debug.Assert(ByteArraysEqual(b, plain), "Cipher.Decrypt{bytes,tdea-CBC} failed")

			Console.WriteLine("TESTING CIPHER(aes128, bytes):")
			' AES using byte arrays
			key = Cnv.FromHex("0123456789ABCDEFF0E1D2C3B4A59687")
			iv = Cnv.FromHex("FEDCBA9876543210FEDCBA9876543210")
			' "Now is the time for all good men" 
			plain = Cnv.FromHex("4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E")
			cipher__1 = Cnv.FromHex("c3153108a8dd340c0bcb1dfe8d25d2320ee0e66bd2bb4a313fb75c5638e9e177")
			b = Cipher.Encrypt(plain, key, iv, CipherAlgorithm.Aes128, Mode.CBC)
			Console.WriteLine("KY={0}", Cnv.ToHex(key))
			Console.WriteLine("IV={0}", Cnv.ToHex(iv))
			Console.WriteLine("PT={0}", Cnv.ToHex(plain))
			Console.WriteLine("CT={0}", Cnv.ToHex(b))
			Console.WriteLine("OK={0}", Cnv.ToHex(cipher__1))
			Debug.Assert(ByteArraysEqual(b, cipher__1), "Cipher.Encrypt{bytes,Aes128-CBC} failed")
			' Decrypt
			b = Cipher.Decrypt(cipher__1, key, iv, CipherAlgorithm.Aes128, Mode.CBC)
			Console.WriteLine("P'={0}", Cnv.ToHex(b))
			Console.WriteLine("OK={0}", Cnv.ToHex(plain))
			Debug.Assert(ByteArraysEqual(b, plain), "Cipher.Decrypt{bytes,Aes128-CBC} failed")

			' Test some errors
			Console.WriteLine("TESTING CIPHER ERRORS:")
			Console.WriteLine("Test an invalid key length...")
			Dim badkey As Byte() = New Byte(22) {}
			b = Cipher.Encrypt(plain, key, Nothing, CipherAlgorithm.Tdea, Mode.ECB)
			Console.Write("Cipher.Encrypt returns '{0}' ", Cnv.ToHex(b))
			n = General.ErrorCode()
			Console.WriteLine("ErrorCode={0} {1}", n, General.ErrorLookup(n))
			Debug.Assert(b.Length = 0, "Should be an error")

			Console.WriteLine("Test an invalid IV length...")
			Dim badiv As Byte() = New Byte(6) {}
			b = Cipher.Encrypt(plain, key, badiv, CipherAlgorithm.Aes128, Mode.CTR)
			Console.Write("Cipher.Encrypt returns '{0}' ", Cnv.ToHex(b))
			n = General.ErrorCode()
			Console.WriteLine("ErrorCode={0} {1}", n, General.ErrorLookup(n))
			Debug.Assert(b.Length = 0, "Should be an error")

			Console.WriteLine("Test invalid length for plaintext...")
			Dim badblk As Byte() = New Byte(30) {}
			b = Cipher.Encrypt(badblk, key, iv, CipherAlgorithm.Aes128, Mode.CBC)
			Console.Write("Cipher.Encrypt returns '{0}' ", Cnv.ToHex(b))
			n = General.ErrorCode()
			Console.WriteLine("ErrorCode={0} {1}", n, General.ErrorLookup(n))
			Debug.Assert(b.Length = 0, "Should be an error")

			Console.WriteLine("Test invalid length for ciphertext...")
			b = Cipher.Decrypt(badblk, key, iv, CipherAlgorithm.Aes128, Mode.CBC)
			Console.Write("Cipher.Decrypt returns '{0}' ", Cnv.ToHex(b))
			n = General.ErrorCode()
			Console.WriteLine("ErrorCode={0} {1}", n, General.ErrorLookup(n))
			Debug.Assert(b.Length = 0, "Should be an error")

			Console.WriteLine("...END TESTING CIPHER ERRORS.")
		End Sub

		Private Shared Sub test_Cipher_Hex()
			Dim s As String
			Dim n As Integer
			Dim keyhex As String, plainStr As String, cipherStr As String, ivhex As String

			Console.WriteLine(vbLf & "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.Aes128, 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.Aes128, Mode.CBC)
			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.")


		End Sub
		Private Shared Sub test_Cipher_Size()
			Dim alg As CipherAlgorithm

			Console.WriteLine(vbLf & "BLOCK CIPHER SIZES:")

			alg = CipherAlgorithm.Aes128
			Console.WriteLine("ALG=" & alg.ToString() & ": Cipher.KeyBytes=" & Cipher.KeyBytes(alg) & ", Cipher.BlockBytes=" & Cipher.BlockBytes(alg))

			alg = CipherAlgorithm.Aes192
			Console.WriteLine("ALG=" & alg.ToString() & ": Cipher.KeyBytes=" & Cipher.KeyBytes(alg) & ", Cipher.BlockBytes=" & Cipher.BlockBytes(alg))

			alg = CipherAlgorithm.Aes256
			Console.WriteLine("ALG=" & alg.ToString() & ": Cipher.KeyBytes=" & Cipher.KeyBytes(alg) & ", Cipher.BlockBytes=" & Cipher.BlockBytes(alg))

			alg = CipherAlgorithm.Tdea
			Console.WriteLine("ALG=" & alg.ToString() & ": Cipher.KeyBytes=" & Cipher.KeyBytes(alg) & ", Cipher.BlockBytes=" & Cipher.BlockBytes(alg))
		End Sub

		Private Shared Sub test_Cipher_File()
			Console.WriteLine(vbLf & "TESTING CIPHER FILE:")
			' Encrypt random data of random length using alg/mode selected at random

			Dim algs As CipherAlgorithm() = New CipherAlgorithm() {CipherAlgorithm.Aes128, CipherAlgorithm.Aes192, CipherAlgorithm.Aes256, CipherAlgorithm.Tdea}
			Dim modes As Mode() = New Mode() {Mode.CBC, Mode.CFB, Mode.CTR, Mode.ECB, Mode.OFB}
			Dim paddings As Padding() = New Padding() {Padding.[Default], Padding.Pkcs5, Padding.OneAndZeroes, Padding.AnsiX923, Padding.W3CPadding}
			Dim opts As Cipher.Opts() = New Cipher.Opts() {Cipher.Opts.[Default], Cipher.Opts.PrefixIV}
			Dim file_pt As String = "file-pt.txt"
			Dim file_ct As String = "file-ct.dat"
			Dim file_ck As String = "file-ck.txt"

			Dim alg As CipherAlgorithm
			Dim mode__1 As Mode
			Dim pad As Padding
			Dim opt As Cipher.Opts
			Dim key As Byte()
			Dim iv As Byte()
			Dim b As Byte()
			Dim buf As Char()
			Dim s As String, s1 As String
			Dim len As Integer, r As Integer, counter As Integer
			Dim ntests As Integer = 5

			Console.WriteLine("NTESTS=" & ntests)
			For counter = 0 To ntests - 1
				' Create a text file of random data of random length
				len = Rng.Number(0, 73)
				Console.WriteLine("Test {0}: random len={1}", counter + 1, len)
				buf = New Char(len - 1) {}
				For i As Integer = 0 To len - 1
					' random printable character in [' ', '~']
					Dim ch As Char = ChrW(Rng.Number(&H20, &H7e))
					buf(i) = ch
				Next
				s = New String(buf)
				Console.WriteLine("PT=[" & s & "]")
				File.WriteAllText(file_pt, s)

				' Read in the ciphertext bytes as a check
				b = File.ReadAllBytes(file_pt)
				Console.WriteLine("PT:" & Cnv.ToHex(b))

				' Choose an algorithm from list
				alg = algs(Rng.Number(0, algs.Length - 1))
				Console.WriteLine("ALG=" & alg.ToString())
				' Choose a mode
				mode__1 = modes(Rng.Number(0, modes.Length - 1))
				Console.WriteLine("MODE=" & mode__1.ToString())
				' Choose type of padding, if relevant
				If mode__1 = Mode.ECB OrElse mode__1 = Mode.CBC Then
					pad = paddings(Rng.Number(0, paddings.Length - 1))
				Else
					' Note: padding is ignored for other modes
					pad = Padding.NoPad
				End If
				Console.WriteLine("PAD=" & pad.ToString())
				' Choose an advanced option
				opt = opts(Rng.Number(0, opts.Length - 1))
				Console.WriteLine("OPT=" & opt.ToString())

				' Generate random key of required length
				key = Rng.Bytes(Cipher.KeyBytes(alg))
				Console.WriteLine("KY:" & Cnv.ToHex(key))
				' Generate IV of required length (ignored for ECB)
				If mode__1 <> Mode.ECB Then
					iv = Rng.Bytes(Cipher.BlockBytes(alg))
					Console.WriteLine("IV:" & Cnv.ToHex(iv))
				Else
					iv = Nothing
				End If

				' Encrypt
				r = Cipher.FileEncrypt(file_ct, file_pt, key, iv, alg, mode__1, _
					pad, opt)
				Console.WriteLine("Cipher.FileEncrypt() returns {0} (expecting 0)", r)
				Debug.Assert(0 = r, "Cipher.FileEncrypt failed.")

				' Read in the ciphertext bytes as a check
				b = File.ReadAllBytes(file_ct)
				Console.WriteLine("CT:" & Cnv.ToHex(b))

				' Decrypt
				If (opt And Cipher.Opts.PrefixIV) = Cipher.Opts.PrefixIV Then
					' IV is not required if already exists prefixed to file
					iv = Nothing
				End If
				r = Cipher.FileDecrypt(file_ck, file_ct, key, iv, alg, mode__1, _
					pad, opt)
				Console.WriteLine("Cipher.FileDecrypt() returns {0} (expecting 0)", r)
				Debug.Assert(0 = r, "Cipher.FileDecrypt failed.")

				' Read in the decrypted plaintext as a check
				s1 = File.ReadAllText(file_ck)
				Console.WriteLine("P1:[" & s & "]")

				Debug.Assert(s1 = s)
			Next

		End Sub

		Private Shared Sub test_KeyWrap()
			' **************
			' KEY WRAPPING
			' **************
			Dim arrPlain As Byte(), arrKey As Byte()
			Dim arrCipher As Byte(), bcheck As Byte()
			Console.WriteLine(vbLf & "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(ByteArraysEqual(bcheck, arrPlain), "Unwrapped key is different.")

		End Sub


		Private Shared Sub test_Autack()
			Dim s As String
			Dim b As Byte()
			Dim msg As Byte()
			Dim hexDigest As String
			Dim strCheck As String
			Dim xmlKey As String, msgStr As String, keyStr As String
			Dim keyBits As Integer
			' *********************
			' AUTACK SIGNATURES 
			' *********************
			' (This is specialist stuff)
			Console.WriteLine(vbLf & "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")


		End Sub

		Private Shared Sub test_PEM()
			Dim s As String
			Dim r As Integer
			Dim fnameInput As String, fnameOutput As String, fnameOutputUnix As String
			Dim hexDigest As String
			Dim strCheck As String

			' *********************
			' PEM FILE CONVERSIONS
			' *********************
			Console.WriteLine(vbLf & "PEM FILE CONVERSIONS:")
			fnameInput = "AliceRSASignByCarl.cer"
			fnameOutput = "AliceRSASignByCarl.pem.cer"
			fnameOutputUnix = "AliceRSASignByCarl.unix.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 = File.ReadAllText(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")

			' Output in Unix/SSL LF line-endings, instead of Windows CR-LF endings.
			r = Pem.FileFromBinFile(fnameOutputUnix, fnameInput, "CERTIFICATE", 72, True)
			Debug.Assert(r = 0, "Pem.FileFromBinFile failed.")
			Console.WriteLine("Created new PEM file {0}", fnameOutputUnix)
			' Read and display
			s = File.ReadAllText(fnameOutput)
			Console.WriteLine("{0}", s)
			' Check these two certs have the same thumbprint
			hexDigest = X509.CertThumb(fnameOutputUnix, HashAlgorithm.Sha1)
			Console.WriteLine("{1}=SHA-1({0})", fnameOutputUnix, 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")


		End Sub

		Private Shared Sub test_Padding()
			Dim s As String
			Dim i As Integer
			Dim b As Byte()

			' *************************
			' PADDING FOR BLOCK CIPHERS
			' *************************
			Console.WriteLine(vbLf & "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)

			' 6. Pad single byte using OneAndZeroes
			Console.WriteLine("OneAndZeroes")
			s = "ff"
			Console.WriteLine("Input   ='{0}'", s)
			s = Cipher.Pad(s, CipherAlgorithm.Aes128, Padding.OneAndZeroes)
			Console.WriteLine("Padded  ='{0}'", s)
			' Now strip the padding
			s = Cipher.Unpad(s, CipherAlgorithm.Aes128, Padding.OneAndZeroes)
			Console.WriteLine("Unpadded='{0}'", s)

		End Sub

		Private Shared Sub test_Cnv_hex()
			Dim s As String
			Dim b As Byte()
			'**************************
			' HEX CONVERSION TESTS *
			'**************************
			Console.WriteLine(vbLf & "HEX CONVERSION TESTS:")

			s = "DEADbeefCAFEBABE01"
			b = Cnv.FromHex(s)
			Console.WriteLine("'{0}'->0x{1}", s, Cnv.ToHex(b))

			' [v11.1] NB This behaviour has changed: punctuation chars like ':' and whitespace are OK, 
			' but letters [G-Zg-z] and other non-hex chars are now an error
			Console.WriteLine("The Cnv.FromHex() method will ignore punctuation characters and whitespace...")
			' Convert a hex string containing ":" and space 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")

			Console.WriteLine("...but will give an error for invalid chars like [G-Zg-z]...")
			s = "FEGCBA9876543210"
			Console.WriteLine("Bad hex = '{0}'", s)
			b = Cnv.FromHex(s)
			Console.WriteLine("Cnv.FromHex() returns '{0}' (expected empty => error)", Cnv.ToHex(b))
			Debug.Assert(b.Length = 0)
			Console.WriteLine(General.FormatErrorMessage())
		End Sub

		Private Shared Sub test_Cnv_base64()
			Dim s As String
			Dim b As Byte()
			Dim b64str As String
			'**************************
			' BASE64 CONVERSION TESTS *
			'**************************
			Console.WriteLine(vbLf & "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)

			' Use a string with invalid base64 characters
			s = "%/ty6m.!*HZUMhA=&é"
			' Try with unfiltered string = an error [New in v11.1]
			Console.WriteLine("Bad base64 = '{0}'", s)
			b = Cnv.FromBase64(s)
			Console.WriteLine("Cnv.FromBase64() returns '{0}' (expected empty => error)", Cnv.ToHex(b))
			Debug.Assert(b.Length = 0)
			Console.WriteLine(General.FormatErrorMessage())
			' Filter out non-base64 chars (%.!*&) and e-acute
			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")


		End Sub

		Private Shared Sub test_Cnv_other()
			Dim s As String
			Dim n As Integer
			Dim b As Byte()
			Dim fname As String
			Dim s1 As String
			Dim b1 As Byte()

			'*******************
			' CONVERSION TESTS *
			'*******************
			Console.WriteLine(vbLf & "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"
			File.WriteAllBytes(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))

			' 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))


		End Sub

		Private Shared Sub test_Cnv_base58()
			Dim s As String
			Dim b As Byte()
			Dim b58str As String
			'**************************
			' BASE58 CONVERSION TESTS *
			'**************************
			' Fixed typos [2021-10-17]
			Console.WriteLine(vbLf & "BASE58 CONVERSION TESTS:")
			b58str = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM"
			b = Cnv.FromBase58(b58str)
			Console.WriteLine("'" & b58str & "'->" & vbLf & "0x" & Cnv.ToHex(b))
			' Back to base58
			s = Cnv.ToBase58(b)
			Console.WriteLine("In base58='" & s & "'")
			Debug.Assert(s = b58str, "Base58 conversion failed")
			' Convert directly from base58 to hex
			s = Cnv.ToHex(Cnv.FromBase58(b58str))
			Console.WriteLine("In hex='" & s & "'")
			Debug.Assert(s = "00010966776006953D5567439E5E39F86A0D273BEED61967F6", "Cnv.FromBase58 failed")
		End Sub

		Private Shared Sub test_CipherPad()
			Dim s As String
			Dim key As Byte(), iv As Byte(), pt As Byte(), ct As Byte(), p1 As Byte(), correct As Byte()

			Console.WriteLine(vbLf & "TEST THE CIPHER CLASS WITH PADDING...")
			Console.WriteLine("Tdea/CBC/Pkcs5")
			key = Cnv.FromHex("737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32")
			iv = Cnv.FromHex("B36B6BFB6231084E")
			Console.WriteLine("KY=" & Cnv.ToHex(key))
			Console.WriteLine("IV=" & Cnv.ToHex(iv))
			pt = Cnv.FromHex("5468697320736F6D652073616D706520636F6E74656E742E")
			Console.WriteLine("PT=" & Cnv.ToHex(pt))
			Console.WriteLine("PT='" & System.Text.Encoding.[Default].GetString(pt) & "'")
			correct = Cnv.FromHex("d76fd1178fbd02f84231f5c1d2a2f74a4159482964f675248254223daf9af8e4")
			ct = Cipher.Encrypt(pt, key, iv, CipherAlgorithm.Tdea, Mode.CBC, Padding.Pkcs5)
			Console.WriteLine("CT=" & Cnv.ToHex(ct))
			Console.WriteLine("OK=" & Cnv.ToHex(correct))
			Debug.Assert(ByteArraysEqual(ct, correct), "Cipher.Encrypt with padding failed")
			' Now decrypt
			p1 = Cipher.Decrypt(ct, key, iv, CipherAlgorithm.Tdea, Mode.CBC, Padding.Pkcs5)
			Console.WriteLine("P'=" & Cnv.ToHex(p1))
			Console.WriteLine("P'='" & System.Text.Encoding.[Default].GetString(p1) & "'")
			Debug.Assert(ByteArraysEqual(p1, pt), "Cipher.Decrypt with padding failed")

			Console.WriteLine("Aes128/CBC/pkcs5")
			key = Cnv.FromHex("0123456789ABCDEFF0E1D2C3B4A59687")
			iv = Cnv.FromHex("FEDCBA9876543210FEDCBA9876543210")
			Console.WriteLine("KY=" & Cnv.ToHex(key))
			Console.WriteLine("IV=" & Cnv.ToHex(iv))
			s = "Now is the time for all good men to"
			pt = System.Text.Encoding.[Default].GetBytes(s)
			Console.WriteLine("PT=" & Cnv.ToHex(pt))
			Console.WriteLine("PT='" & System.Text.Encoding.[Default].GetString(pt) & "'")
			correct = Cnv.FromHex("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B")
			ct = Cipher.Encrypt(pt, key, iv, CipherAlgorithm.Aes128, Mode.CBC, Padding.Pkcs5)
			Console.WriteLine("CT=" & Cnv.ToHex(ct))
			Console.WriteLine("OK=" & Cnv.ToHex(correct))
			Debug.Assert(ByteArraysEqual(ct, correct), "Cipher.Encrypt with padding failed")
			' Now decrypt
			p1 = Cipher.Decrypt(ct, key, iv, CipherAlgorithm.Aes128, Mode.CBC, Padding.Pkcs5)
			Console.WriteLine("P'=" & Cnv.ToHex(p1))
			Console.WriteLine("P'='" & System.Text.Encoding.[Default].GetString(p1) & "'")
			Debug.Assert(ByteArraysEqual(p1, pt), "Cipher.Decrypt with padding failed")
			p1 = Cipher.Decrypt(ct, key, iv, CipherAlgorithm.Aes128, Mode.CBC, Padding.NoPad)
			Console.WriteLine("Pn=" & Cnv.ToHex(p1))

			Console.WriteLine("Aes128/ECB/OneAndZeroes...")
			ct = Cipher.Encrypt(pt, key, iv, CipherAlgorithm.Aes128, Mode.ECB, Padding.OneAndZeroes)
			Console.WriteLine("CT=" & Cnv.ToHex(ct))
			p1 = Cipher.Decrypt(ct, key, iv, CipherAlgorithm.Aes128, Mode.ECB, Padding.NoPad)
			Console.WriteLine("Pn=" & Cnv.ToHex(p1))
			p1 = Cipher.Decrypt(ct, key, iv, CipherAlgorithm.Aes128, Mode.ECB, Padding.OneAndZeroes)
			Console.WriteLine("P'=" & Cnv.ToHex(p1))
			Console.WriteLine("P'='" & System.Text.Encoding.[Default].GetString(p1) & "'")
			Debug.Assert(ByteArraysEqual(p1, pt))


		End Sub

		Private Shared Sub test_Pbkdf2()
			'***************
			' PBKDF2 TESTS *
			'***************
			Dim n As Integer
			Dim arrKey As Byte()
			Dim strCheck As String
			Dim keyStr As String
			Dim arrPwd As Byte(), salt As Byte()
			Dim saltHex As String

			Console.WriteLine(vbLf & "TESTING PBKDF2...")
			' convert password string to bytes
			arrPwd = System.Text.Encoding.[Default].GetBytes("password")
			' make a salt
			salt = New Byte() {&H78, &H57, &H8e, &H5a, &H5d, &H63, _
				&Hcb, &H6}
			' create a 24-byte (192-bit) key
			n = 24
			arrKey = Pbe.Kdf2(n, arrPwd, salt, 2048)
			Debug.Assert(arrKey.Length > 0, "ERROR with Pbe.Kdf2")
			Console.WriteLine("Key({0})={1}", n * 8, Cnv.ToHex(arrKey))
			' and again for a 64-byte (512-bit) key
			n = 64
			arrKey = Pbe.Kdf2(n, arrPwd, salt, 2048)
			Debug.Assert(arrKey.Length > 0, "ERROR with Pbe.Kdf2")
			Console.WriteLine("Key({0})={1}", n * 8, Cnv.ToHex(arrKey))
			' Same example using hex format
			saltHex = "78578e5a5d63cb06"
			n = 24
			strCheck = "BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643"
			keyStr = Pbe.Kdf2(n, "password", saltHex, 2048)
			Debug.Assert(keyStr.Length > 0, "ERROR with PbeKdf2/Hex")
			Console.WriteLine("Key({0})={1}", n * 8, keyStr)
			' Compare with known test vector
			Debug.Assert([String].Compare(strCheck, keyStr, True) = 0, "PBKDF Derived key {HMAC-SHA-1} does not match test vector")

			' Use different hash function (SHA-256) in KDF
			Console.WriteLine("Using SHA-256 in KDF...")
			n = 24
			arrKey = Pbe.Kdf2(n, arrPwd, salt, 2048, HashAlgorithm.Sha256)
			Debug.Assert(arrKey.Length > 0, "ERROR with Pbe.Kdf2")
			Console.WriteLine("Key({0})={1}", n * 8, Cnv.ToHex(arrKey))
			' using hex format
			saltHex = "78578e5a5d63cb06"
			n = 24
			strCheck = "97B5A91D35AF542324881315C4F849E327C4707D1BC9D322"
			keyStr = Pbe.Kdf2(n, "password", saltHex, 2048, HashAlgorithm.Sha256)
			Debug.Assert(keyStr.Length > 0, "ERROR with PbeKdf2/Hex")
			Console.WriteLine("Key({0})={1}", n * 8, keyStr)
			' Compare with known test vector
			Debug.Assert([String].Compare(strCheck, keyStr, True) = 0, "PBKDF Derived key {HMAC-SHA-256} does not match test vector")

			' And again using SHA-224 in KDF
			Console.WriteLine("Using SHA-224 in KDF...")
			n = 24
			arrKey = Pbe.Kdf2(n, arrPwd, salt, 2048, HashAlgorithm.Sha224)
			Debug.Assert(arrKey.Length > 0, "ERROR with Pbe.Kdf2")
			Console.WriteLine("Key({0})={1}", n * 8, Cnv.ToHex(arrKey))
			' using hex format
			saltHex = "78578e5a5d63cb06"
			n = 24
			strCheck = "10CFFEDFB13503519969151E466F587028E0720B387F9AEF"
			keyStr = Pbe.Kdf2(n, "password", saltHex, 2048, HashAlgorithm.Sha224)
			Debug.Assert(keyStr.Length > 0, "ERROR with PbeKdf2/Hex")
			Console.WriteLine("Key({0})={1}", n * 8, keyStr)
			' Compare with known test vector
			Debug.Assert([String].Compare(strCheck, keyStr, True) = 0, "PBKDF Derived key {HMAC-SHA-224} does not match test vector")


		End Sub

		Private Shared Sub test_Asn1()
			'***************
			' ASN.1 TESTS *
			'***************
			Dim s As String
			Dim fname As String
			Dim b64str As String
			' Alice's RSA public key as a base64 string
			b64str = "MIGJAoGBAOCJczmN2PX16Id2OX9OsAW7U4PeD7er3H3HdSkNBS5tEt+mhibU" & "0m+qWCn8l+z6glEPMIC+sVCeRkTxLLvYMs/GaG8H2bBgrL7uNAlqE/X3BQWT" & "3166NVbZYf8Zf8mB5vhs6odAcO+sbSx0ny36VTq5mXcCpkhSjE7zVzhXdFdf" & "AgMBAAE="

			Console.WriteLine(vbLf & "TESTING ASN.1...")
			fname = "smallca.cer"
			Console.WriteLine("FILE: {0}", fname)
			s = Asn1.TextDumpToString(fname, 0)
			Debug.Assert(s.Length > 0, "Asn1.TextDumpToString failed")
			Console.WriteLine(s)

			' Pass data as a base64 string
			s = "MAQwAgUA"
			' Very simple ASN.1 data
			Console.WriteLine("STRING: '{0}'", s)
			s = Asn1.TextDumpToString("MAQwAgUA", 0)
			Debug.Assert(s.Length > 0, "Asn1.TextDumpToString failed")
			Console.WriteLine(s)

			' Again, but without comments
			Console.WriteLine("Again, without comments...")
			s = Asn1.TextDumpToString("MAQwAgUA", Asn1.Options.NoComments)
			Debug.Assert(s.Length > 0, "Asn1.TextDumpToString failed")
			Console.WriteLine(s)

			Console.WriteLine("Pass data as base64 string...")
			Console.WriteLine("DATA='" & b64str & "'")
			s = Asn1.TextDumpToString(b64str, 0)
			Debug.Assert(s.Length > 0, "Asn1.TextDumpToString failed")
			Console.WriteLine(s)

			Console.WriteLine("Print type names of various ASN.1 data files...")
			fname = "smallca.cer"
			s = Asn1.Type(fname)
			Console.WriteLine("Asn1.Type('" & fname & "')=" & s)
			fname = "AlicePrivRSASign.p8e"
			s = Asn1.Type(fname)
			Console.WriteLine("Asn1.Type('" & fname & "')=" & s)
			fname = "rfc3280bis_CRL.crl"
			s = Asn1.Type(fname)
			Console.WriteLine("Asn1.Type('" & fname & "')=" & s)
			fname = "bob.p7b"
			s = Asn1.Type(fname)
			Console.WriteLine("Asn1.Type('" & fname & "')=" & s)
			fname = "ocsp_response_ok_dims.dat"
			s = Asn1.Type(fname)
			Console.WriteLine("Asn1.Type('" & fname & "')=" & s)
			fname = b64str
			s = Asn1.Type(fname)
			Console.WriteLine("Asn1.Type('" & fname & "')=" & s)

		End Sub

		Private Shared Sub test_Sig()
			Dim n As Integer
			Dim b As Byte()
			Dim s As String, correct_sha1 As String, correct_sha256 As String
			Dim keyFile As String, password As String, certFile As String, dataFile As String

			'***************
			' SIG TESTS *
			'***************
			Console.WriteLine(vbLf & "TESTING SIG...")

			keyFile = "AlicePrivRSASign.p8e"
			' Used to sign data
			certFile = "AliceRSASignByCarl.cer"
			' Used to verify signature
			password = "password"

			' Create RSA signatures over the string "abc"
			' -- these are all known values we can check for a given private key

			b = System.Text.Encoding.[Default].GetBytes("abc")
			' Set byte array with ASCII string "abc"
			Console.WriteLine("data=(0x)" & Cnv.ToHex(b) & " (" & b.Length & " bytes)")

			Console.WriteLine("Alice signs 'abc' using default SHA-1 with RSA")
			' Alice/'abc'/SHA-1
			correct_sha1 = "YK1aePtKQDDsVCyJdM0V9VOE6DZVTO3ZoyLV9BNcYmep0glwxU5mUQcLAUTUOETImTIN2Pp4Gffr" & "xqdxUoczLshnXBNhg7P4ofge+WlBgmcTCnVv27LHHZpmdEbjTg6tnPMb+2b4FvMZ0LfkMKXyiRVTmG4A" & "NyAmHH6QIsDZ8R8="
			s = Sig.SignData(b, keyFile, password, SigAlgorithm.[Default])
			Console.WriteLine("Sig.signData returns " & vbLf & "'" & s & "'")
			Debug.Assert(s.Equals(correct_sha1), "Sig.signData failed")
			' Now verify the signature we just made using Alice's certificate
			n = Sig.VerifyData(s, b, certFile, SigAlgorithm.[Default])
			Console.WriteLine("Sig.verifyData returns " & n & " (expecting 0)")
			Debug.Assert(0 = n, "Sig.verifyData failed")

			Console.WriteLine("Alice signs 'abc' using SHA-256 with RSA")
			' Alice/'abc'/SHA-256
			correct_sha256 = "tLy6hJadL4w9JI/A/qLCG0Vz1fWPIrPMWD8NGmA5wP7HHlUID54elztUYrpdm9RFeh0RCMJ618dw" & "BpgIutuZg2qQ2i9uMUYB0DvZvkyeD6MqmtVa4ihgc9SLqhigKeP+KB7voEi7PH3hpEr9Wa3kn4mbPpeD" & "1VHSzgu/qirjOaA="
			s = Sig.SignData(b, keyFile, password, SigAlgorithm.Rsa_Sha256)
			Console.WriteLine("Sig.signData returns " & vbLf & "'" & s & "'")
			Debug.Assert(s.Equals(correct_sha256), "Sig.signData failed")
			' Now verify the signature we just made
			n = Sig.VerifyData(s, b, certFile, SigAlgorithm.Rsa_Sha256)
			Console.WriteLine("Sig.verifyData returns " & n & " (expecting 0)")
			Debug.Assert(0 = n, "Sig.verifyData failed")

			Console.WriteLine("Alice signs 'abc' using digest value")
			' Known SHA-1 digest value of 'abc'
			b = Cnv.FromHex("a9993e364706816aba3e25717850c26c9cd0d89d")
			s = Sig.SignDigest(b, keyFile, password, SigAlgorithm.Rsa_Sha1)
			Console.WriteLine("Sig.signDigest returns " & vbLf & "'" & s & "'")
			Debug.Assert(s.Equals(correct_sha1), "Sig.signDigest failed")
			' Now verify the signature we just made
			n = Sig.VerifyDigest(s, b, certFile, SigAlgorithm.Rsa_Sha1)
			Console.WriteLine("Sig.verifyData returns " & n & " (expecting 0)")
			Debug.Assert(0 = n, "Sig.verifyData failed")

			Console.WriteLine("Alice signs 'abc' using SHA-256 digest value")
			' Known SHA-256 digest value of 'abc'
			b = Cnv.FromHex("ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad")
			s = Sig.SignDigest(b, keyFile, password, SigAlgorithm.Rsa_Sha256)
			Console.WriteLine("Sig.signDigest returns " & vbLf & "'" & s & "'")
			Debug.Assert(s.Equals(correct_sha256), "Sig.signDigest failed")
			' Now verify the signature we just made
			n = Sig.VerifyDigest(s, b, certFile, SigAlgorithm.Rsa_Sha256)
			Console.WriteLine("Sig.verifyData returns " & n & " (expecting 0)")
			Debug.Assert(0 = n, "Sig.verifyData failed")

			' Create a file containing exactly the 3-char string "abc"
			dataFile = "abc.txt"
			File.WriteAllText(dataFile, "abc")
			Console.WriteLine("Alice signs file containing 'abc' using SHA-1 with RSA")
			s = Sig.SignFile(dataFile, keyFile, password, SigAlgorithm.Rsa_Sha1)
			Console.WriteLine("Sig.signDigest returns " & vbLf & "'" & s & "'")
			Debug.Assert(s.Equals(correct_sha1), "Sig.signDigest failed")
			' Now verify the signature we just made
			n = Sig.VerifyFile(s, dataFile, certFile, SigAlgorithm.Rsa_Sha1)
			Console.WriteLine("Sig.VerifyData returns " & n & " (expecting 0)")
			Debug.Assert(0 = n, "Sig.VerifyData failed")

		End Sub

		Private Shared Function checkSig(sig64 As [String], sigAlg As SigAlgorithm, publicKeyStr As [String]) As [Boolean]
			' An obscure check that we can reproduce the signature using Rsa.EncodeDigestForSignature
			' (for our own testing purposes - you don't need to do this in practice)
			' 1. Convert base64-encoded signature to a byte array
			Dim sig__1 As Byte() = Cnv.FromBase64(sig64)
			' 2. Decrypt using internal public key string
			Dim b As Byte() = Rsa.RawPublic(sig__1, publicKeyStr)
			Console.WriteLine("Decrypted block:")
			Console.WriteLine(Cnv.ToHex(b))
			Debug.Assert(b.Length > 0)
			' 3. Extract the message digest from decrypted block
			' -- this should be the digest of "abc"
			Dim digest As Byte() = Rsa.DecodeDigestForSignature(b)
			Console.WriteLine("digest=" & Cnv.ToHex(digest))
			' 4. Now encode for signature using specified digest algorithm
			' -- this should match exactly
			Dim hashAlg As HashAlgorithm = Sig.GetHashAlgFromSigAlg(sigAlg)
			Dim b1 As Byte() = Rsa.EncodeDigestForSignature(b.Length, digest, hashAlg)
			Console.WriteLine("Re-encoded block with " & Convert.ToString(hashAlg) & ":")
			Console.WriteLine(Cnv.ToHex(b1))
			Debug.Assert(ByteArraysEqual(b, b1), "Blocks are not equal")

			Return True
		End Function

		Private Shared Sub test_Sig_Vars()
			Dim r As Integer
			Dim b As Byte()
			Dim s As [String]
			Dim keyFile As [String], password As [String], certFile As [String]
			Dim publicKeyStr As [String]
			Dim sigAlg As SigAlgorithm

			Console.WriteLine(vbLf & "TESTING SIG VARS...")

			keyFile = "AlicePrivRSASign.p8e"
			' Used to sign data
			certFile = "AliceRSASignByCarl.cer"
			' Used to verify signature
			password = "password"
			' CAUTION: DO NOT HARD-CODE REAL PASSWORDS
			b = System.Text.Encoding.[Default].GetBytes("abc")
			' Set byte array with ASCII string "abc"
			Console.WriteLine("data=(0x)" & Cnv.ToHex(b) & " (" & b.Length & " bytes)")
			publicKeyStr = Rsa.ReadPublicKey(certFile).ToString()

			sigAlg = SigAlgorithm.Rsa_Sha1
			s = Sig.SignData(b, keyFile, password, sigAlg)
			Console.WriteLine("Sig.signData(" & Convert.ToString(sigAlg) & ") returns " & vbLf & s)
			checkSig(s, sigAlg, publicKeyStr)
			r = Sig.VerifyData(s, b, publicKeyStr, sigAlg)
			Console.WriteLine("Sig.VerifyData returns " & r & " (expected 0)")
			Debug.Assert(0 = r)

			sigAlg = SigAlgorithm.Rsa_Sha224
			s = Sig.SignData(b, keyFile, password, sigAlg)
			Console.WriteLine("Sig.signData(" & Convert.ToString(sigAlg) & ") returns " & vbLf & s)
			checkSig(s, sigAlg, publicKeyStr)
			r = Sig.VerifyData(s, b, publicKeyStr, sigAlg)
			Console.WriteLine("Sig.VerifyData returns " & r & " (expected 0)")
			Debug.Assert(0 = r)

			sigAlg = SigAlgorithm.Rsa_Sha256
			s = Sig.SignData(b, keyFile, password, sigAlg)
			Console.WriteLine("Sig.signData(" & Convert.ToString(sigAlg) & ") returns " & vbLf & s)
			checkSig(s, sigAlg, publicKeyStr)
			r = Sig.VerifyData(s, b, publicKeyStr, sigAlg)
			Console.WriteLine("Sig.VerifyData returns " & r & " (expected 0)")
			Debug.Assert(0 = r)

			sigAlg = SigAlgorithm.Rsa_Sha384
			s = Sig.SignData(b, keyFile, password, sigAlg)
			Console.WriteLine("Sig.signData(" & Convert.ToString(sigAlg) & ") returns " & vbLf & s)
			checkSig(s, sigAlg, publicKeyStr)
			r = Sig.VerifyData(s, b, publicKeyStr, sigAlg)
			Console.WriteLine("Sig.VerifyData returns " & r & " (expected 0)")
			Debug.Assert(0 = r)

			sigAlg = SigAlgorithm.Rsa_Sha512
			s = Sig.SignData(b, keyFile, password, sigAlg)
			Console.WriteLine("Sig.signData(" & Convert.ToString(sigAlg) & ") returns " & vbLf & s)
			checkSig(s, sigAlg, publicKeyStr)
			r = Sig.VerifyData(s, b, publicKeyStr, sigAlg)
			Console.WriteLine("Sig.VerifyData returns " & r & " (expected 0)")
			Debug.Assert(0 = r)

			sigAlg = SigAlgorithm.Rsa_Md5
			s = Sig.SignData(b, keyFile, password, sigAlg)
			Console.WriteLine("Sig.signData(" & Convert.ToString(sigAlg) & ") returns " & vbLf & s)
			checkSig(s, sigAlg, publicKeyStr)
			r = Sig.VerifyData(s, b, publicKeyStr, sigAlg)
			Console.WriteLine("Sig.VerifyData returns " & r & " (expected 0)")
			Debug.Assert(0 = r)

		End Sub

		Private Shared Sub test_Sig_Ecc()
			Dim n As Integer
			Dim s As String
			Dim b As Byte()
			Dim digest As Byte()
			Dim sig__1 As String
			Dim fname As String
			Dim prikeyfile As String = "prime256v1.p8"
			Dim pubkeyfile As String = "prime256v1.pub"
			Dim sigAlg As SigAlgorithm = SigAlgorithm.Ecdsa_Sha256
			Dim sbPassword As New StringBuilder("password")
			Dim sb As StringBuilder
			Dim hashAlg As HashAlgorithm

			Console.WriteLine(vbLf & "TESTING SIG ECC...")

			' 1. Make a new 256-bit keypair using P-256 curve (`Prime256v1` is a synonym)
			n = Ecc.MakeKeys(pubkeyfile, prikeyfile, Ecc.CurveName.Prime256v1, sbPassword.ToString())
			Console.WriteLine("Ecc.MakeKeys returns " & n & " (expected 0)")

			' 1a. Query the keys we just made, just to check
			sb = Ecc.ReadPrivateKey(prikeyfile, sbPassword.ToString())
			s = Ecc.QueryKey(sb.ToString(), "curveName")
			Console.WriteLine("CurveName: " & s)
			s = Ecc.QueryKey(sb.ToString(), "keyBits")
			Console.WriteLine("KeyBits:" & s)
			Wipe.[String](sb)

			' A. Sign the DATA
			Console.WriteLine("SigAlgorithm: " & sigAlg.ToString())

			' 2. Prepare input data as an array of bytes
			b = System.Text.Encoding.[Default].GetBytes("abc")
			Console.WriteLine("DATA: " & System.Text.Encoding.[Default].GetString(b))

			' 3. Sign DATA using the private key we made above
			sig__1 = Sig.SignData(b, prikeyfile, sbPassword.ToString(), sigAlg)
			' NB signature value will be different each time
			Console.WriteLine("SIG: " & sig__1)

			' 4. Verify that the signature over the DATA
			n = Sig.VerifyData(sig__1, b, pubkeyfile, sigAlg)
			Console.WriteLine("Sig.VerifyData returns " & n & " (expected 0)")
			Debug.Assert(0 = n)


			' B. Sign the DIGEST of the data

			' Get the hash algorithm to use
			hashAlg = Sig.GetHashAlgFromSigAlg(sigAlg)
			Console.WriteLine("HashAlgorithm: " & hashAlg.ToString())
			' Compute the digest value
			digest = Hash.BytesFromBytes(b, hashAlg)
			Console.WriteLine("DIGEST: " & Cnv.ToHex(digest))

			' Sign the DIGEST
			sig__1 = Sig.SignDigest(digest, prikeyfile, sbPassword.ToString(), sigAlg)
			Console.WriteLine("SIG: " & sig__1)

			' And verify the signature using the digest value
			n = Sig.VerifyDigest(sig__1, digest, pubkeyfile, sigAlg)
			Console.WriteLine("Sig.VerifyDigest returns " & n & " (expected 0)")
			Debug.Assert(0 = n)


			' C. Sign a FILE containing the data

			fname = "abc.txt"
			Console.WriteLine("FILE: " & fname)
			File.WriteAllBytes(fname, b)

			' Sign the FILE
			sig__1 = Sig.SignFile(fname, prikeyfile, sbPassword.ToString(), sigAlg)
			Console.WriteLine("SIG: " & sig__1)

			' And verify the signature over the file
			n = Sig.VerifyFile(sig__1, fname, pubkeyfile, sigAlg)
			Console.WriteLine("Sig.VerifyFile returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			' Finally, wipe the password
			Wipe.[String](sbPassword)
		End Sub

		Private Shared Sub test_Sig_Ecc_det()
			Dim sig__1 As String, intKey As String, intPubKey As String
			Dim b As Byte()
			Dim n As Integer
			Dim hexKey As String
			Dim curveName As Ecc.CurveName
			Dim sigdetok As String = "0f2141a0ebbc44d2e1af90a50ebcfce5e197b3b7d4de036deb18bc9e1f3d7387500cb99cf5f7c157070a8961e38700b7"

			Console.WriteLine(vbLf & "TESTING SIG ECC DETERMINISTIC...")

			' Ref: [RFC6979] "Deterministic Usage of the DSA and ECDSA"
			' A.2.3.  ECDSA, 192 Bits (Prime Field)

			' 1. READ IN PRIVATE KEY IN (HEX,CURVENAME) FORMAT
			hexKey = "6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4"
			curveName = Ecc.CurveName.P_192
			Console.WriteLine("KEYHEX: " & hexKey)
			Console.WriteLine("CURVE:  " & curveName.ToString())
			intKey = Ecc.ReadKeyByCurve(hexKey, curveName)
			'Console.WriteLine("INTERNAL KEY (different each time!):\n[" + intKey + "]");

			' 2. PREPARE INPUT DATA AS AN ARRAY OF BYTES
			b = System.Text.Encoding.[Default].GetBytes("test")

			' 3. SIGN IT USING DETERMINISTIC ALGORITHM FROM [RFC6979]
			'    with output in hex encoding
			'    -- deterministic method always gives the same signature value for same input
			sig__1 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1, Sig.SigOptions.UseDeterministic, Sig.Encoding.Base16)
			Console.WriteLine("SIG(hex): " & sig__1)
			Console.WriteLine("SIG(OK) : " & sigdetok)
			Debug.Assert([String].Compare(sig__1, sigdetok, True) = 0, "Sig.SignData failed")

			' 4. VERIFY IT WITH THE PUBLIC KEY
			intPubKey = Ecc.PublicKeyFromPrivate(intKey)
			n = Sig.VerifyData(Cnv.ToBase64(Cnv.FromHex(sig__1)), b, intPubKey, SigAlgorithm.Ecdsa_Sha1)
			Console.WriteLine("Sig.VerifyData returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			' SIGN IT AGAIN WITH SIG IN ASN1DER FORM
			sig__1 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1, Sig.SigOptions.UseDeterministic Or Sig.SigOptions.Asn1DERStructure, Sig.Encoding.Base16)
			Console.WriteLine("SIG(asn1der): " & sig__1)
			' VERIFY IT AGAIN
			n = Sig.VerifyData(sig__1, b, intPubKey, SigAlgorithm.Ecdsa_Sha1)
			Console.WriteLine("Sig.VerifyData returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			' SIGN IT AGAIN WITH SIG IN ASN1DER FORM AND ENCODE IN DEFAULT BASE64
			sig__1 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1, Sig.SigOptions.UseDeterministic Or Sig.SigOptions.Asn1DERStructure, 0)
			Console.WriteLine("SIG(asn1der): " & sig__1)
			' VERIFY IT AGAIN
			n = Sig.VerifyData(sig__1, b, intPubKey, SigAlgorithm.Ecdsa_Sha1)
			Console.WriteLine("Sig.VerifyData returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			' SIGN IT AGAIN WITH SIG ENCODED IN "URL_SAFE" BASE64URL
			sig__1 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1, Sig.SigOptions.UseDeterministic Or Sig.SigOptions.Asn1DERStructure, Sig.Encoding.Base64url)
			Console.WriteLine("SIG(base64url): " & sig__1)
			' VERIFY IT AGAIN (note the encoding is detected automatically by Sig.Verifydata)
			n = Sig.VerifyData(sig__1, b, intPubKey, SigAlgorithm.Ecdsa_Sha1)
			Console.WriteLine("Sig.VerifyData returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			' SIGN IT USING DEFAULT NON-DETERMINISTIC METHOD (different each time)
			sig__1 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1)
			Console.WriteLine("SIG(random-k): " & sig__1)
			' VERIFY IT AGAIN
			n = Sig.VerifyData(sig__1, b, intPubKey, SigAlgorithm.Ecdsa_Sha1)
			Console.WriteLine("Sig.VerifyData returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

		End Sub

		Private Shared Sub test_Sig_Ecc_btc()
			Dim r As Integer
			Dim s As String
			Dim sig__1 As String, internalKey As String, internalPubKey As String
			Dim hexKey As String
			Dim curveName As Ecc.CurveName
			Dim datahex As String
			Dim data As Byte(), digest As Byte()
			Dim sigweb As String = "3045022100da43201760bda697222002f56266bf65023fef2094519e13077f777baed553b102205ce35d05eabda58cd50a67977a65706347cc25ef43153e309ff210a134722e9e"
			Dim pubkeyok As String = "042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9"
			Dim sigdetok As String = "30450220587ce0cf0252e2db3a7c3c91b355aa8f3385e128227cd8727c5f7777877ad772022100edc508b7c14891ed15ab38c687019d7ebaf5c12908cf21a83e8ae57e8c47e95c"

			Console.WriteLine(vbLf & "TESTING SIG ECC FOR BITCOIN RAW EXAMPLE...")

			' Ref: http://bitcoin.stackexchange.com/questions/32628/redeeming-a-raw-transaction-step-by-step-example-required
			' https://blockchain.info/tx/211b8fb30990632751a83d1dc4f0323ff7d2fd3cad88084de13c9be2ae1c6426
			' ?format=hex

			' READ IN PRIVATE KEY IN (HEX,CURVENAME) FORMAT
			hexKey = "0ecd20654c2e2be708495853e8da35c664247040c00bd10b9b13e5e86e6a808d"
			curveName = Ecc.CurveName.Secp256k1
			Console.WriteLine("KEYHEX: " & hexKey)
			Console.WriteLine("CURVE:  " & curveName.ToString())
			internalKey = Ecc.ReadKeyByCurve(hexKey, curveName)
			' Derive public key in hex form from given private key and check against known value
			s = Ecc.QueryKey(internalKey, "publicKey")
			Console.WriteLine("Ecc.QueryKey('publicKey')=" & vbLf & s)
			Console.WriteLine("OK PUBKEY:" & vbLf & pubkeyok)
			Debug.Assert(s = pubkeyok)

			' THE RAW_TX DATA TO BE SIGNED (derived from blockchain info from link above)
			'
'				01000000
'				01
'				be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d5396
'				00000000
'				19 76 a9 14 dd6cce9f255a8cc17bda8ba0373df8e861cb866e 88 ac
'				ffffffff
'				01
'				23ce010000000000
'				19 76 a9 14 2bc89c2702e0e618db7d59eb5ce2f0f147b40754 88 ac
'				00000000
'				01000000
'			

			datahex = "0100000001be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d5396000000001976a914dd6cce9f255a8cc17bda8ba0373df8e861cb866e88acffffffff0123ce0100000000001976a9142bc89c2702e0e618db7d59eb5ce2f0f147b4075488ac0000000001000000"
			data = Cnv.FromHex(datahex)
			Console.WriteLine("RAW_TX:" & vbLf & Cnv.ToHex(data))

			' COMPUTE THE DOUBLE SHA-256 DIGEST OF THE RAW DATA
			digest = Hash.[Double](data, HashAlgorithm.Sha256)
			Console.WriteLine("SHA256(SHA256(RAW_TX)):" & vbLf & Cnv.ToHex(digest))
			Console.WriteLine("Same hash value but reversed:" & vbLf & Cnv.ToHex(Cnv.ReverseBytes(digest)))

			' Sign using ECDSA deterministic method of RFC6979
			' -- NOTE this will not be the same as the published signature (which used a random k we cannot reproduce) 
			' but the deterministic method should always produce the same signature value:
			' 30450220587ce0cf0252e2db3a7c3c91b355aa8f3385e128227cd8727c5f7777877ad772022100edc508b7c14891ed15ab38c687019d7ebaf5c12908cf21a83e8ae57e8c47e95c
			sig__1 = Sig.SignDigest(digest, internalKey, "", SigAlgorithm.Ecdsa_Sha256, Sig.SigOptions.UseDeterministic Or Sig.SigOptions.Asn1DERStructure, Sig.Encoding.Base16)
			Console.WriteLine("SIG (DET): " & sig__1)
			Debug.Assert(sig__1 = sigdetok)

			' Generate the public key from the private in internal key string form. 
			' -- we use this to verify the signature.
			internalPubKey = Ecc.PublicKeyFromPrivate(internalKey)

			' Now we verify over the same data using (a) our signature and (b) the one from the web page
			' -- they both should work.

			' (a) Use the deterministic signature `sig` we created above
			r = Sig.VerifyDigest(sig__1, digest, internalPubKey, SigAlgorithm.Ecdsa_Sha256)
			Console.WriteLine("Sig.VerifyDigest returns " & r & " (expected 0)")
			Debug.Assert(0 = r)

			' (b) Use the signature `sigweb` from the published Bitcoin transaction web page
			Console.WriteLine("SIG (TX): " & sigweb)
			r = Sig.VerifyDigest(sigweb, digest, internalPubKey, SigAlgorithm.Ecdsa_Sha256)
			Console.WriteLine("Sig.VerifyDigest returns " & r & " (expected 0)")
			Debug.Assert(0 = r)

		End Sub

		Private Shared Sub test_Ecc_MakeKeys()
			Dim n As Integer
			Dim s As String
			Dim pubkeyfile As String
			Dim prikeyfile As String
			Dim sbIntKey As StringBuilder
			Dim h1 As Integer, h2 As Integer

			Console.WriteLine(vbLf & "TESTING ECC MAKEKEYS...")

			Console.WriteLine("Create a new pair of ECC keys using 'Bitcoin' curve, saving private key with default parameters")
			pubkeyfile = "myeckeyk256.pub"
			prikeyfile = "myeckeyk256.p8"
			n = Ecc.MakeKeys(pubkeyfile, prikeyfile, Ecc.CurveName.Secp256k1, "password")
			Console.WriteLine("Ecc.MakeKeys returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			s = Asn1.Type(pubkeyfile)
			Console.WriteLine("'" & pubkeyfile & "'-->" & s)
			s = Asn1.Type(prikeyfile)
			Console.WriteLine("'" & prikeyfile & "'-->" & s)

			Console.WriteLine("Create a new pair of ECC keys using P-192 curve, saving private key with stronger encryption")
			pubkeyfile = "myeckeyp192.pub"
			prikeyfile = "myeckeyp192.p8"
			n = Ecc.MakeKeys(pubkeyfile, prikeyfile, Ecc.CurveName.Prime192v1, "password", Ecc.PbeScheme.Pbe_Pbkdf2_aes128_CBC, "count=3999;prf=hmacWithSha256", _
				Ecc.Format.PEM)
			Console.WriteLine("Ecc.MakeKeys returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			Console.WriteLine("Display some info about the key we just made...")
			s = Asn1.Type(pubkeyfile)
			Console.WriteLine("'" & pubkeyfile & "'-->" & s)
			s = Asn1.Type(prikeyfile)
			Console.WriteLine("'" & prikeyfile & "'-->" & s)

			asn1DumpFile(prikeyfile)

			' Read in the private key to an internal key string, then query it
			sbIntKey = Ecc.ReadPrivateKey(prikeyfile, "password")
			s = Ecc.QueryKey(sbIntKey.ToString(), "keybits")
			Console.WriteLine("QueryKey('keyBits')=" & s)
			s = Ecc.QueryKey(sbIntKey.ToString(), "publicKey")
			Console.WriteLine("QueryKey('publicKey')=" & vbLf & "[" & s & "]")
			h1 = Ecc.KeyHashCode(sbIntKey.ToString())
			Console.WriteLine("Ecc.KeyhashCode(private)=0x{0:X8}", h1)
			' Read in the public key to an internal key string, then check its HashCode
			sbIntKey = Ecc.ReadPublicKey(pubkeyfile)
			h2 = Ecc.KeyHashCode(sbIntKey.ToString())
			Console.WriteLine("Ecc.KeyhashCode(public) =0x{0:X8}", h2)
			Debug.Assert(h1 = h2)

			Wipe.[String](sbIntKey)

		End Sub

		Private Shared Sub test_ECC_Brainpool()
			Dim n As Integer
			Dim s As String
			Dim pubkeyfile As String
			Dim prikeyfile As String
			Dim sbIntKey As StringBuilder
			Dim h1 As Integer, h2 As Integer
			Dim sigval As String
			Dim r As Integer

			Console.WriteLine(vbLf & "TESTING ECC BRAINPOOL...")

			Console.WriteLine("Create a new pair of ECC keys using Brainpool curve, saving private key with stronger encryption")
			pubkeyfile = "myeckeyBrainpool256.pub"
			prikeyfile = "myeckeyBrainpool256.p8e"
			n = Ecc.MakeKeys(pubkeyfile, prikeyfile, Ecc.CurveName.BrainpoolP256r1, "password", Ecc.PbeScheme.Pbe_Pbkdf2_aes128_CBC, "count=3999;prf=hmacWithSha256", _
				Ecc.Format.PEM)
			Console.WriteLine("Ecc.MakeKeys returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			Console.WriteLine("Display some info about the key we just made...")
			s = Asn1.Type(pubkeyfile)
			Console.WriteLine("'" & pubkeyfile & "'-->" & s)
			s = Asn1.Type(prikeyfile)
			Console.WriteLine("'" & prikeyfile & "'-->" & s)

			asn1DumpFile(pubkeyfile)

			' Read in the private key to an internal key string, then query it
			sbIntKey = Ecc.ReadPrivateKey(prikeyfile, "password")
			s = Ecc.QueryKey(sbIntKey.ToString(), "keybits")
			Console.WriteLine("QueryKey('keyBits')=" & s)
			s = Ecc.QueryKey(sbIntKey.ToString(), "curveName")
			Console.WriteLine("QueryKey('curveName')=" & s)
			s = Ecc.QueryKey(sbIntKey.ToString(), "publicKey")
			Console.WriteLine("QueryKey('publicKey')=" & vbLf & "[" & s & "]")
			h1 = Ecc.KeyHashCode(sbIntKey.ToString())
			Console.WriteLine("Ecc.KeyhashCode(private)=0x{0:X8}", h1)
			' Read in the public key to an internal key string, then check its HashCode
			sbIntKey = Ecc.ReadPublicKey(pubkeyfile)
			h2 = Ecc.KeyHashCode(sbIntKey.ToString())
			Console.WriteLine("Ecc.KeyhashCode(public) =0x{0:X8}", h2)
			Debug.Assert(h1 = h2)

			Wipe.[String](sbIntKey)

			Console.WriteLine(vbLf & "Again using stronger 512-bit algorithms...")
			pubkeyfile = "myeckeyBrainpool512.pub"
			prikeyfile = "myeckeyBrainpool512.p8e"
			n = Ecc.MakeKeys(pubkeyfile, prikeyfile, Ecc.CurveName.BrainpoolP512r1, "password", Ecc.PbeScheme.Pbe_Pbkdf2_aes256_CBC, "count=5999;prf=hmacWithSha512")
			Console.WriteLine("Ecc.MakeKeys returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			Console.WriteLine("Display some info about the key we just made...")
			s = Asn1.Type(pubkeyfile)
			Console.WriteLine("'" & pubkeyfile & "'-->" & s)
			s = Asn1.Type(prikeyfile)
			Console.WriteLine("'" & prikeyfile & "'-->" & s)

			asn1DumpFile(pubkeyfile)

			' Read in the private key to an internal key string, then query it
			sbIntKey = Ecc.ReadPrivateKey(prikeyfile, "password")
			s = Ecc.QueryKey(sbIntKey.ToString(), "keybits")
			Console.WriteLine("QueryKey('keyBits')=" & s)
			s = Ecc.QueryKey(sbIntKey.ToString(), "curveName")
			Console.WriteLine("QueryKey('curveName')=" & s)
			s = Ecc.QueryKey(sbIntKey.ToString(), "publicKey")
			Console.WriteLine("QueryKey('publicKey')=" & vbLf & "[" & s & "]")
			h1 = Ecc.KeyHashCode(sbIntKey.ToString())
			Console.WriteLine("Ecc.KeyhashCode(private)=0x{0:X8}", h1)
			' Read in the public key to an internal key string, then check its HashCode
			sbIntKey = Ecc.ReadPublicKey(pubkeyfile)
			h2 = Ecc.KeyHashCode(sbIntKey.ToString())
			Console.WriteLine("Ecc.KeyhashCode(public) =0x{0:X8}", h2)
			Debug.Assert(h1 = h2)
			Wipe.[String](sbIntKey)

			Console.WriteLine("Sign 'abc' using ECDSA...")
			Dim msg As Byte() = System.Text.Encoding.[Default].GetBytes("abc")
			Console.WriteLine("MSG: {0}", Cnv.ToHex(msg))
			sigval = Sig.SignData(msg, prikeyfile, "password", SigAlgorithm.Ecdsa_Sha512, Sig.SigOptions.UseDeterministic)
			If sigval.Length = 0 Then
				Console.WriteLine(General.FormatErrorMessage())
			End If
			Console.WriteLine("Sig={0}", sigval)
			' Verify the signature
			r = Sig.VerifyData(sigval, msg, pubkeyfile, SigAlgorithm.Ecdsa_Sha512)
			Console.WriteLine("Sig.VerifyData returns {0} (expected 0)", r)

		End Sub

		Private Shared Sub test_Ecc_MakeReadSaveKeys()
			Dim n As Integer
			Dim s As String
			Dim pubkeyfile As String
			Dim prikeyfile As String, fname As String
			Dim sbIntKey As StringBuilder
			Dim query As String

			Console.WriteLine(vbLf & "TESTING ECC MAKEREADSAVEKEYS...")

			Console.WriteLine("Create a new pair of ECC keys using P-521 curve")
			pubkeyfile = "myeckey521.pub"
			prikeyfile = "myeckey521.p8"
			n = Ecc.MakeKeys(pubkeyfile, prikeyfile, Ecc.CurveName.P_521, "password")
			Console.WriteLine("Ecc.MakeKeys returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			s = Asn1.Type(pubkeyfile)
			Console.WriteLine("'" & pubkeyfile & "'-->" & s)
			s = Asn1.Type(prikeyfile)
			Console.WriteLine("'" & prikeyfile & "'-->" & s)

			' Read in the private key to an internal key string, then save it without any encryption (!)
			sbIntKey = Ecc.ReadPrivateKey(prikeyfile, "password")
			Console.WriteLine("Ecc.ReadPrivateKey returns a string of length " & sbIntKey.Length & " (expected >0)")
			Debug.Assert(sbIntKey.Length > 0)
			query = "curveName"
			s = Ecc.QueryKey(sbIntKey.ToString(), query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)

			Console.WriteLine("Save private key without any encryption")
			fname = "myeckey521plain.key"
			n = Ecc.SaveKey(fname, sbIntKey.ToString(), 0, Ecc.Format.PEM)
			Console.WriteLine("Ecc.SaveKey(default) returns " & n & " (expected 0)")
			Debug.Assert(0 = n)
			s = Asn1.Type(fname)
			Console.WriteLine("'" & fname & "'-->" & s)

			' Read in the private key we just saved, just to show we can
			sbIntKey = Ecc.ReadPrivateKey(fname, "")
			Console.WriteLine("Ecc.ReadPrivateKey returns a string of length " & sbIntKey.Length & " (expected >0)")
			Debug.Assert(sbIntKey.Length > 0)
			query = "curveName"
			s = Ecc.QueryKey(sbIntKey.ToString(), query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)

			Console.WriteLine("Save private key in PKCS8 format")
			fname = "myeckey521pkcs8.key"
			n = Ecc.SaveKey(fname, sbIntKey.ToString(), Ecc.KeyType.Pkcs8PrivateKeyInfo, 0)
			Console.WriteLine("Ecc.SaveKey(pkcs8) returns " & n & " (expected 0)")
			Debug.Assert(0 = n)

			' Read in the private key we just saved, just to show we can
			sbIntKey = Ecc.ReadPrivateKey(fname, "")
			Console.WriteLine("Ecc.ReadPrivateKey returns a string of length " & sbIntKey.Length & " (expected >0)")
			Debug.Assert(sbIntKey.Length > 0)
			query = "isPrivate"
			s = Ecc.QueryKey(sbIntKey.ToString(), query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
			query = "privateKey"
			s = Ecc.QueryKey(sbIntKey.ToString(), query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)

			' Read in the public key file to an internal key string
			Console.WriteLine("Read in the public key file to an internal key string")
			sbIntKey = Ecc.ReadPublicKey(pubkeyfile)
			Console.WriteLine("Ecc.ReadPublicKey returns a string of length " & sbIntKey.Length & " (expected >0)")
			Debug.Assert(sbIntKey.Length > 0)
			query = "isPrivate"
			s = Ecc.QueryKey(sbIntKey.ToString(), query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
			query = "privateKey"
			s = Ecc.QueryKey(sbIntKey.ToString(), query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
		End Sub


		Private Shared Sub test_Ecc_KeyByCurve()
			Dim s As String
			Dim hexKey As String
			Dim curveName As Ecc.CurveName
			Dim internalKey As String
			Dim query As String
			Dim b58Key As String

			Console.WriteLine(vbLf & "TESTING ECC READKEYBYCURVE...")

			Console.WriteLine("1. A NIST P-192 public key in X9.63 uncompressed format")
			hexKey = "0496C248BE456192FA1380CCF615D171452F41FF31B92BA733524FD77168DEA4425A3EA8FD79B98DC7AFE83C86DCC39A96"
			curveName = Ecc.CurveName.Prime192v1
			' A synonym for P-192
			Console.WriteLine("KEYHEX: " & hexKey)
			Console.WriteLine("CURVE: " & Convert.ToString(curveName))

			' Read into internal key string
			internalKey = Ecc.ReadKeyByCurve(hexKey, curveName)
			' Find the key size in bits
			query = "keyBits"
			s = Ecc.QueryKey(internalKey, query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
			Debug.Assert(Convert.ToInt32(s) = 192)
			' Is it a private or public key?
			query = "isPrivate"
			s = Ecc.QueryKey(internalKey, query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
			Debug.Assert(Convert.ToInt32(s) = 0)

			Console.WriteLine("2. A Bitcoin private key in base58 form")
			b58Key = "6ACCbmy9qwiFcuVgvxNNwMPfoghobzznWrLs3v7t3RmN"
			curveName = Ecc.CurveName.Secp256k1
			Console.WriteLine("KEYB58: " & b58Key)
			' Convert base58 to a hex string
			hexKey = Cnv.ToHex(Cnv.FromBase58(b58Key))
			Console.WriteLine("KEYHEX: " & hexKey)
			Console.WriteLine("CURVE: " & Convert.ToString(curveName))
			' Read into internal key string
			internalKey = Ecc.ReadKeyByCurve(hexKey, curveName)
			' Find the key size in bits
			query = "keyBits"
			s = Ecc.QueryKey(internalKey, query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
			Debug.Assert(Convert.ToInt32(s) = 256)
			' Is it a private or public key?
			query = "isPrivate"
			s = Ecc.QueryKey(internalKey, query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
			Debug.Assert(Convert.ToInt32(s) = 1)

			' Extract the public key in hex form from the private key
			query = "publicKey"
			s = Ecc.QueryKey(internalKey, query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
			Debug.Assert(s.Length > 0)

		End Sub

		Private Shared Sub test_Ecc_SaveKey()
			Dim n As Integer
			Dim s As String
			Dim hexKey As String
			Dim curveName As Ecc.CurveName
			Dim intPriKey As String, intPubKey As String
			Dim query As String
			Dim fname As String

			Console.WriteLine(vbLf & "TESTING ECC SAVEKEYS...")

			Console.WriteLine("1. Read in a 'Bitcoin' private key in hex form")
			hexKey = "0ecd20654c2e2be708495853e8da35c664247040c00bd10b9b13e5e86e6a808d"
			curveName = Ecc.CurveName.Secp256k1
			Console.WriteLine("KEYHEX: " & hexKey)
			Console.WriteLine("CURVE: " & Convert.ToString(curveName))

			' Read into an internal key string
			intPriKey = Ecc.ReadKeyByCurve(hexKey, curveName)

			' Check we got what we expect
			query = "keyBits"
			s = Ecc.QueryKey(intPriKey, query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
			Debug.Assert(Convert.ToInt32(s) = 256)
			query = "curveName"
			s = Ecc.QueryKey(intPriKey, query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)
			query = "isPrivate"
			s = Ecc.QueryKey(intPriKey, query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)

			Console.WriteLine("2. Get the public key from the private key")
			intPubKey = Ecc.PublicKeyFromPrivate(intPriKey)
			query = "isPrivate"
			s = Ecc.QueryKey(intPubKey, query)
			Console.WriteLine("QueryKey('" & query & "')=" & s)

			Console.WriteLine("3. Save the public and private keys in various forms...")
			Console.WriteLine(" 3a. There is only one format to save public keys: SubjectPublicKeyInfo [RFC5480]")
			fname = "mybitcoinkey.pub"
			n = Ecc.SaveKey(fname, intPubKey, 0, 0)
			Console.WriteLine("Ecc.SaveKey returns " & n & " (expected 0)")
			' Get ASN.1 type
			s = Asn1.Type(fname)
			Console.WriteLine("'" & fname & "'-->" & s)

			Console.WriteLine(" 3b. There is only one *encrypted* format to save private keys: PKCS#8 EncryptedPrivateKeyInfo [RFC5208]")
			fname = "mybitcoinkey_enc.p8"
			n = Ecc.SaveEncKey(fname, intPriKey, "password", 0, "", 0)
			Console.WriteLine("Ecc.SaveEncKey returns " & n & " (expected 0)")
			' Get ASN.1 type
			s = Asn1.Type(fname)
			Console.WriteLine("'" & fname & "'-->" & s)

			Console.WriteLine(" 3c. There are two *unencrypted* formats to save private keys:")
			Console.WriteLine("  3c(i). ECPrivateKey [RFC5915]")
			fname = "mybitcoinkey_ec.key"
			n = Ecc.SaveKey(fname, intPriKey, 0, 0)
			Console.WriteLine("Ecc.SaveKey(default) returns " & n & " (expected 0)")
			' Get ASN.1 type
			s = Asn1.Type(fname)
			Console.WriteLine("'" & fname & "'-->" & s)

			Console.WriteLine("  3c(ii). PKCS#8 PrivateKeyInfo [RFC5208]")
			fname = "mybitcoinkey.p8"
			n = Ecc.SaveKey(fname, intPriKey, Ecc.KeyType.Pkcs8PrivateKeyInfo, 0)
			Console.WriteLine("Ecc.SaveKey(default) returns " & n & " (expected 0)")
			' Get ASN.1 type
			s = Asn1.Type(fname)
			Console.WriteLine("'" & fname & "'-->" & s)

		End Sub

		' BYTE UTILITIES
		Private Shared Sub test_Byte_Utils()
			Dim b As Byte()
			' NOTE: this is only place we explicitly use an unsigned integer
			Dim n As UInteger

			Console.WriteLine(vbLf & "TESTING BYTE UTILS...")

			Console.WriteLine("Testing Cnv.ReverseBytes()...")
			b = New Byte(4) {&H1, &H2, &H3, &H4, &H5}
			Console.WriteLine("(before)=" & Cnv.ToHex(b))
			b = Cnv.ReverseBytes(b)
			Console.WriteLine("(after)= " & Cnv.ToHex(b))
			Debug.Assert(b(0) = &H5 AndAlso b(4) = &H1, "Cnv.ReverseBytes failed")
			b = New Byte(3) {&H1, &H2, &H3, &H4}
			Console.WriteLine("(before)=" & Cnv.ToHex(b))
			b = Cnv.ReverseBytes(b)
			Console.WriteLine("(after)= " & Cnv.ToHex(b))
			Debug.Assert(b(0) = &H4 AndAlso b(3) = &H1, "Cnv.ReverseBytes failed")
			b = New Byte(0) {&H1}
			Console.WriteLine("(before)=" & Cnv.ToHex(b))
			b = Cnv.ReverseBytes(b)
			Console.WriteLine("(after)= " & Cnv.ToHex(b))
			Debug.Assert(b(0) = &H1, "Cnv.ReverseBytes failed")

			Console.WriteLine("Testing Cnv.NumToBytes()...")
			b = Cnv.NumToBytes(&HdeadbeefUI, Cnv.EndianNess.BigEndian)
			Console.WriteLine("(0xdeadbeef,BE)->" & Cnv.ToHex(b))
			b = Cnv.NumToBytes(&HdeadbeefUI, Cnv.EndianNess.LittleEndian)
			Console.WriteLine("(0xdeadbeef,LE)->" & Cnv.ToHex(b))

			Console.WriteLine("Testing Cnv.NumFromBytes()...")
			b = New Byte(3) {&Hde, &Had, &Hbe, &Hef}
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.BigEndian)
			Console.WriteLine("(" & Cnv.ToHex(b) & ",BE)->0x" & n.ToString("x8"))
			Debug.Assert(&HdeadbeefUI = n)
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.LittleEndian)
			Console.WriteLine("(" & Cnv.ToHex(b) & ",LE)->0x" & n.ToString("x8"))
			Debug.Assert(&HefbeaddeUI = n)
			b = Cnv.FromHex("DEAD")
			' Short array
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.BigEndian)
			Console.WriteLine("(" & Cnv.ToHex(b) & ",BE)->0x" & n.ToString("x8"))
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.LittleEndian)
			Console.WriteLine("(" & Cnv.ToHex(b) & ",LE)->0x" & n.ToString("x8"))
			b = Cnv.FromHex("EF")
			' Shorter array
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.BigEndian)
			Console.WriteLine("(" & Cnv.ToHex(b) & ",BE)->0x" & n.ToString("x8"))
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.LittleEndian)
			Console.WriteLine("(" & Cnv.ToHex(b) & ",LE)->0x" & n.ToString("x8"))
			b = Cnv.FromHex("DEADBEEFCAFEBABE")
			' Longer array
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.BigEndian)
			Console.WriteLine("(" & Cnv.ToHex(b) & ",BE)->0x" & n.ToString("x8"))
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.LittleEndian)
			Console.WriteLine("(" & Cnv.ToHex(b) & ",LE)->0x" & n.ToString("x8"))

		End Sub

		Private Shared Sub test_Compress()
			Console.WriteLine(vbLf & "COMPRESS DATA USING ZLIB COMPRESSION:")
			Dim data As Byte(), cdata As Byte(), udata As Byte()
			' Put data into a byte array
			data = System.Text.Encoding.UTF8.GetBytes("hello, hello, hello. This is a 'hello world' message for the world, repeat, for the world.")
			' Compress it
			cdata = Compr.Compress(data)
			Console.WriteLine("Compressed {0} bytes to {1}", data.Length, cdata.Length)

			' Now decompress it
			udata = Compr.Uncompress(cdata)
			Console.WriteLine("Uncompressed length is {0}", udata.Length)

			' Show data as an ascii string
			Console.WriteLine("[{0}]", System.Text.Encoding.UTF8.GetString(udata))

		End Sub

		'************************************
		' CODE FORMERLY IN TestPKISharpV12.cs
		'************************************

		Private Shared Sub test_MakeRSA_ECCKeys()
			' Demonstrates:
'             * Rsa.MakeKeys()   (Updated [v20.3])
'             * Ecc.MakeKeys()
'             

			Dim r As Integer
			Console.WriteLine("Generating two 2048-bit RSA keys...")
			r = Rsa.MakeKeys("CA_RSA_2048.pub", "CA_RSA_2048.p8e", "password", 2048, pbes := Rsa.PbeOptions.Pbe_Pbkdf2_aes256_CBC)
			r = Rsa.MakeKeys("User_RSA_2048.pub", "User_RSA_2048.p8e", "password", 2048, pbes := Rsa.PbeOptions.Pbe_Pbkdf2_aes256_CBC)

			Console.WriteLine("Generating two ECC keys using P-256...")
			r = Ecc.MakeKeys("CA_ECC_P256.pub", "CA_ECC_P256.p8e", Ecc.CurveName.P_256, "password", Ecc.PbeScheme.Pbe_Pbkdf2_aes256_CBC, "", _
				0)
			r = Ecc.MakeKeys("User_ECC_P256.pub", "User_ECC_P256.p8e", Ecc.CurveName.P_256, "password", Ecc.PbeScheme.Pbe_Pbkdf2_aes256_CBC, "", _
				0)
		End Sub

		Private Shared Sub test_ReadRSA_ECCKeys()
			Console.WriteLine(vbLf & "Show we can read the key files we made...")

			' Demonstrates:
'             * Rsa.ReadPrivateKey()
'             * Rsa.ReadPublicKey()
'             * Rsa.KeyBits()
'             * Rsa.KeyHashCode()
'             * Rsa.KeyMatch()
'             * Ecc.ReadPrivateKey()
'             * Ecc.ReadPublicKey()
'             * Ecc.QueryKey()
'             * Ecc.KeyHashCode()
'             


			Dim sbPriKey As StringBuilder
			Dim pubkey As String
			Dim r As Integer

			' RSA keys:
			sbPriKey = Rsa.ReadPrivateKey("CA_RSA_2048.p8e", "password")
			Debug.Assert(sbPriKey.Length > 0, "Failed to read CA_RSA_2048 private key")
			Console.WriteLine("CA_RSA pri key length={0}", Rsa.KeyBits(sbPriKey.ToString()))
			Console.WriteLine("CA_RSA pri key hash code={0,8:X}", Rsa.KeyHashCode(sbPriKey.ToString()))
			pubkey = Rsa.ReadPublicKey("CA_RSA_2048.pub").ToString()
			Debug.Assert(pubkey.Length > 0, "Failed to read CA_RSA_2048 public key")
			Console.WriteLine("CA_RSA pub key length={0}", Rsa.KeyBits(pubkey))
			Console.WriteLine("CA_RSA pub key hash code={0,8:X}", Rsa.KeyHashCode(pubkey))
			' Check they match
			r = Rsa.KeyMatch(sbPriKey.ToString(), pubkey)
			Console.WriteLine("Rsa.KeyMatch() returns {0} (expected 0)", r)
			Debug.Assert(0 = r)

			' ECC keys:
			sbPriKey = Ecc.ReadPrivateKey("CA_ECC_P256.p8e", "password")
			Debug.Assert(sbPriKey.Length > 0, "Failed to read CA_ECC_P256 private key")
			Console.WriteLine("CA_ECC pri key length={0}", Ecc.QueryKey(sbPriKey.ToString(), "keyBits"))
			Console.WriteLine("CA_ECC pri key hash code={0,8:X}", Ecc.KeyHashCode(sbPriKey.ToString()))
			pubkey = Ecc.ReadPublicKey("CA_ECC_P256.pub").ToString()
			Debug.Assert(pubkey.Length > 0, "Failed to read CA_ECC_P256 public key")
			Console.WriteLine("CA_ECC pub key length={0}", Ecc.QueryKey(pubkey, "keyBits"))
			Console.WriteLine("CA_ECC pub key hash code={0,8:X}", Ecc.KeyHashCode(pubkey))
		End Sub

		Private Shared Sub test_MakeCerts_ECDSA()
			Console.WriteLine(vbLf & "Create a new CA certificate and end-user certificates using ECDSA...")

			' Demonstrates:
'             * X509.MakeCertSelf() using SigAlgorithm.Ecdsa_Sha256
'             * X509.MakeCert() using SigAlgorithm.Ecdsa_Sha256 and X509.CertOptions.Ecdsa_Deterministic
'             * X509.CertRequest() using SigAlgorithm.Ecdsa_Sha256
'             * X509.VerifyCert() for an X.509 certificate
'             * X509.VerifyCert() for a PKCS#10 CSR
'             * X509.VerifyCert() for an X.509 CRL
'             * X509.MakeCRL() using SigAlgorithm.Ecdsa_Sha256
'             * X509.CheckCertInCRL()
'             * X509.TextDumpToString() using X509.OutputOpts.Ldap
'             * Asn1.TextDumpToString()
'             * Ecc.ReadPublicKey()
'             


			Dim r As Integer
			Dim kuFlags As X509.KeyUsageOptions
			Dim dn As String, extns As String
			Dim s As String, query As String, pubkey As String

			' Create a self-signed CA certificate
			Dim ca_cert As String = "CA_ECC_P256.cer"
			kuFlags = X509.KeyUsageOptions.KeyCertSign Or X509.KeyUsageOptions.CrlSign Or X509.KeyUsageOptions.NonRepudiation
			dn = "C=AU;OU=Elliptical;O=Cert Services;CN=El Jefe"
			extns = "serialNumber=#xECC0CA;notBefore=2018-01-01;notAfter=2023-12-31"
			r = X509.MakeCertSelf(ca_cert, "CA_ECC_P256.p8e", 0, 0, dn, extns, _
				kuFlags, "password", SigAlgorithm.Ecdsa_Sha256, X509.CertOptions.UTF8String)
			Console.WriteLine("X509.MakeCertSelf returns {0} (expecting 0)", r)
			Debug.Assert(0 = r)

			' Display cert details (with distinguished name in LDAP form (NB reverse order to dn above) and serial number in Decimal
			Console.WriteLine("FILE: {0}", ca_cert)
			s = X509.TextDumpToString(ca_cert, X509.OutputOpts.[Decimal] Or X509.OutputOpts.Ldap)
			Console.WriteLine(s)

			' Use the CA certificate to create an end-user certificate
			Dim user_cert As String = "User_ECC_P256.cer"
			kuFlags = X509.KeyUsageOptions.DataEncipherment Or X509.KeyUsageOptions.DigitalSignature Or X509.KeyUsageOptions.KeyEncipherment
			dn = "C=AU;OU=Elliptical;O=User Org;CN=The User"
			extns = "serialNumber=#xECD5A0;notBefore=2018-01-02;notAfter=2023-12-30"
			r = X509.MakeCert(user_cert, ca_cert, "User_ECC_P256.pub", "CA_ECC_P256.p8e", 0, 0, _
				dn, extns, kuFlags, "password", SigAlgorithm.Ecdsa_Sha256, X509.CertOptions.UTF8String Or X509.CertOptions.Ecdsa_Deterministic)
			Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r)
			Debug.Assert(0 = r)

			' Query cert for information
			Console.WriteLine("FILE: {0}", user_cert)
			query = "signatureAlgorithm"
			Console.WriteLine("X509.QueryCert('{0}')={1}", query, X509.QueryCert(user_cert, query))
			query = "serialNumber"
			Console.WriteLine("X509.QueryCert('{0}')={1}", query, X509.QueryCert(user_cert, query))
			' Extract public key from certificate
			pubkey = Ecc.ReadPublicKey(user_cert).ToString()
			Debug.Assert(pubkey.Length > 0, "Failed to read public key from certificate")
			Console.WriteLine("ECC public key length={0}", Ecc.QueryKey(pubkey, "keyBits"))

			' Validate the end user cert was issued by CA
			r = X509.VerifyCert(user_cert, ca_cert)
			Console.WriteLine("X509.VerifyCert returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

			Console.WriteLine(vbLf & "Create a PKCS10 certificate signing request (CSR) using ECC P-256 key then use to issue new X.509 certificate...")

			Dim user_csr As String = "User2_ECC_P256.p10"
			dn = "C=AU;OU=Elliptic;O=User Org;CN=User 2"
			extns = "keyUsage=digitalSignature,dataEncipherment,dataEncipherment;"
			' Make a CSR - must match ECC key with ECC signature algorithm
			r = X509.CertRequest(user_csr, "User2_ECC_P256.p8e", dn, extns, "password", SigAlgorithm.Ecdsa_Sha256, _
				0)
			Console.WriteLine("X509.CertRequest returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)
			' We can verify that the signature is good
			r = X509.VerifyCert(user_csr, "")
			Console.WriteLine("X509.VerifyCert('{1}') returns {0} (expecting 0)", r, user_csr)
			Debug.Assert(0 = r)

			' CA receives the CSR and issues a new end-user certificate
			Dim user2_cert As String = "User2_ECC_P256.cer"
			extns = "notBefore=2018-01-02;"
			r = X509.MakeCert(user2_cert, ca_cert, user_csr, "CA_ECC_P256.p8e", &Hecd5a2, 4, _
				"", extns, 0, "password", SigAlgorithm.Ecdsa_Sha256, 0)
			Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

			' Dump the ASN.1 structure of the user certificate
			Console.WriteLine("FILE: {0}", user2_cert)
			s = Asn1.TextDumpToString(user2_cert, 0)
			Console.WriteLine(s)

			Console.WriteLine(vbLf & "The CA creates a Certificate Revocation List (CRL) revoking User2's certificate as of 1 April 2018")
			Dim crlFile As String = "ECC_P256.crl"
			r = X509.MakeCRL(crlFile, ca_cert, "CA_ECC_P256.p8e", "password", "#xECD5A2,2018-04-01", "", _
				SigAlgorithm.Ecdsa_Sha256, 0)
			Console.WriteLine("X509.MakeCRL returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

			' Verify that the signature in the CRL file is good
			r = X509.VerifyCert(crlFile, ca_cert)
			Console.WriteLine("X509.VerifyCert('{1}') returns {0} (expecting 0)", r, crlFile)
			Debug.Assert(0 = r)

			' See if certificate has been revoked as of today
			r = X509.CheckCertInCRL(user2_cert, crlFile, Nothing, "")
			Console.WriteLine("X509.CheckCertInCRL('{1}',now) returns {0} (expecting {2}=X509.Revoked)", r, crlFile, X509.Revoked)
			Debug.Assert(X509.Revoked = r)
			' See if certificate has been revoked as of 2018-02-01
			Dim datestr As String = "2018-02-01"
			r = X509.CheckCertInCRL(user2_cert, crlFile, Nothing, datestr)
			Console.WriteLine("X509.CheckCertInCRL('{1}',{2}) returns {0} (expecting 0=NOT REVOKED)", r, crlFile, datestr)
			Debug.Assert(0 = r)


		End Sub

		Private Shared Sub test_MakeCerts_PSS()
			Console.WriteLine(vbLf & "Create a new CA certificate and end-user certificate using RSA-PSS...")

			' Demonstrates:
'             * X509.MakeCertSelf() using SigAlgorithm.Rsa_Pss_Sha256
'             * X509.MakeCert() using SigAlgorithm.Rsa_Pss_Sha256
'             * X509.QueryCert()
'             * X509.ValidatePath()
'             * X509.TextDumpToString() using X509.OutputOpts.Ldap
'             * Rsa.ReadPublicKey()
'             * Rsa.KeyBits()
'             


			Dim r As Integer
			Dim kuFlags As X509.KeyUsageOptions
			Dim dn As String, extns As String
			Dim s As String, query As String, pubkey As String

			' Create a self-signed CA certificate
			Dim ca_cert As String = "CA_RSA_2048.cer"
			kuFlags = X509.KeyUsageOptions.KeyCertSign Or X509.KeyUsageOptions.CrlSign Or X509.KeyUsageOptions.NonRepudiation
			dn = "C=AU;OU=PSS;O=Cert Services;CN=El Jefe"
			extns = "serialNumber=#d1234567;notBefore=2018-01-01;notAfter=2023-12-31"
			r = X509.MakeCertSelf(ca_cert, "CA_RSA_2048.p8e", 0, 0, dn, extns, _
				kuFlags, "password", SigAlgorithm.Rsa_Pss_Sha256, X509.CertOptions.UTF8String)
			Console.WriteLine("X509.MakeCertSelf returns {0} (expecting 0)", r)
			Debug.Assert(0 = r)

			' Display cert details (with distinguished name in LDAP form (NB reverse order to dn above) and serial number in Decimal
			Console.WriteLine("FILE: {0}", ca_cert)
			s = X509.TextDumpToString(ca_cert, X509.OutputOpts.[Decimal] Or X509.OutputOpts.Ldap)
			Console.WriteLine(s)

			' Use the CA certificate to create an end-user certificate
			Dim user_cert As String = "User_RSA_2048.cer"
			kuFlags = X509.KeyUsageOptions.DataEncipherment Or X509.KeyUsageOptions.DigitalSignature Or X509.KeyUsageOptions.KeyEncipherment
			dn = "C=AU;OU=PSS;O=User Org;CN=The User"
			extns = "serialNumber=#d2345678;notBefore=2018-01-02;notAfter=2023-12-30"
			r = X509.MakeCert(user_cert, ca_cert, "User_RSA_2048.pub", "CA_RSA_2048.p8e", 0, 0, _
				dn, extns, kuFlags, "password", SigAlgorithm.Rsa_Pss_Sha256, X509.CertOptions.UTF8String)
			Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r)
			Debug.Assert(0 = r)

			' Query cert for information
			Console.WriteLine("FILE: {0}", user_cert)
			query = "signatureAlgorithm"
			Console.WriteLine("X509.QueryCert('{0}')={1}", query, X509.QueryCert(user_cert, query))
			query = "serialNumber"
			Console.WriteLine("X509.QueryCert('{0}')={1}", query, X509.QueryCert(user_cert, query, X509.OutputOpts.[Decimal]))
			' Extract public key from certificate
			pubkey = Rsa.ReadPublicKey(user_cert).ToString()
			Debug.Assert(pubkey.Length > 0, "Failed to read public key from certificate")
			Console.WriteLine("Rsa public key length={0}", Rsa.KeyBits(pubkey))

			' Validate the certificate path
			Dim certpath As String = ca_cert & ";" & user_cert
			r = X509.ValidatePath(certpath)
			Console.WriteLine("X509.ValidatePath('{0}') returns {1} (expecting 0)", certpath, r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

		End Sub

		Private Shared Sub test_SIG_VerifyData_PSS()
			Console.WriteLine(vbLf & "VERIFY A SIGNATURE VALUE:")

			' Demonstrates:
'             * Sig.VerifyData() using SigAlgorithm.Rsa_Pss_Sha1 and SigAlgorithm.Rsa_Pss_Sha512
'             * Sig.VerifyDigest()
'             


			Dim r As Integer
			Dim rsakeyvalue As String, msghex As String, S As String
			Dim rsaprikey As String
			Dim msg As Byte(), digest As Byte()

			Console.WriteLine("1. NIST test CAVS 11.1 FIPS186-3 - SigVer RSA PKCS#1 RSASSA-PSS...")
			' Example No 1 from NIST test
			' # CAVS 11.1
			' # "FIPS186-3 - SigVer RSA PKCS#1 RSASSA-PSS" information for "rsa2_check"
			' CryptoSys-PKI-specific way to read in an RSA key using hex-encoded components
			rsakeyvalue = "<RSAKeyValue>" & "<Modulus EncodingType=""hexBinary"">ec996bc93e81094436fd5fc2eef511782eb40fe60cc6f27f24bc8728d686537f1caa82cfcfa5c323604b6918d7cd0318d98395c855c7c7ada6fc447f192283cdc81e7291e232336019d4dac12356b93a349883cd2c0a7d2eae9715f1cc6dd657cea5cb2c46ce6468794b326b33f1bff61a00fa72931345ca6768365e1eb906dd</Modulus>" & "<Exponent EncodingType=""hexBinary"">90c6d3</Exponent></RSAKeyValue>"
			msghex = "a4daf4621676917e28493a585d9baffca3755e77e1f18e3ccfb3dec60ab8ee7e684f5cde8864f2d7ae041d70ce1ea1b1e7878cbf93416848dbfdb5214fde972e5780cb83c439dfc8aa9fa3e2724adbd02bdb36d2213c84d1b12a23fb5bf1baae19772a97ef7cc21bc420b3f570a6c321167745f9b46a489ff8420f9a5679c1c4"
			S = "319c62984acd52423e59a17d27d4eca7722703b054a71a1ee5f7a218b6f4a274632eaf8ef2a577a7e8a7f654b8deb1ec9b1e529cf93459cc8af4c6df6fffabc3edded0c421604ea2aae35836b05fd9de7abd78540d45fd6d0ea714733a3427b00d9d6404db8ede4a27932b47d88243eefcbffe1e55841823def30c57de7562cf"

			msg = Cnv.FromHex(msghex)
			r = Sig.VerifyData(S, msg, rsakeyvalue, SigAlgorithm.Rsa_Pss_Sha1)
			Console.WriteLine("Sig.VerifyData() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

			' Example from NIST with SHA512 and sLen = 0
			Console.WriteLine("2. NIST test with SHA512 and sLen = 0...")
			rsakeyvalue = "<RSAKeyValue>" & "<Modulus EncodingType=""hexBinary"">a3f2235ad2053b4c83fa38f8284ed805421621fe98845fb01b689f5b82b32511b6d16173e7b40a66a3a999c189beb9e06822150ac8be677186370c823b5277d909de07564e281cca2f13873d9d07b7bd85a2b9ac66f4ce4f5e38b8e9eebec04c8caf311e375d69e80851d559b8e90e85ba6b96476790f727c25aa8163062ec8543fcc7759be62c7768ecc37f340bb06102762bf0441ca1aa2c7a81bf37dc8b27439d3abba93812c9bb44fe4d6a94baae709379f5ce5d0c8f81d00086b9caa3026819588f491b525807899cdab33d8e992150d2b105d3aab615217c6a3d740831c7dc76faabd9c9b9817ead0b494566de1433fff5ba4604c6b8446f6fc35e746aff84ff8bd7500410d10e82bf4c9036489de47dee9a327a5c4510d8561321b91d55559a4cba85e0c361767084b25217e8a63c4e151a1e88689feecffd16fa0a65ae41d2babca99cf1b959c3c076c0f75974146f2cc494126fbecad4217b9aaa00f169fa512527ff5a0b50da46d6be870ecef2af7a1e6c4556f6f7a0a00b9f47cb</Modulus>" & "<Exponent EncodingType=""hexBinary"">000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b3f57f</Exponent></RSAKeyValue>"
			msghex = "be2f3e1dc8a3711570401bd535185426944d094e8481a12a438de07d54760c88c99d4fdbbe355d6a26fa56e3ca20ee3f8e8acb98f63d2f3aea14d6fcb6b522d155c3759aef56de3ea0a8f9fd7b111001cf358636a87c765c99c2975bb95063d6ec0b780264ec3eb967b0caca52d10294deb402d3a224bfb9d9ffea41662f18c0"
			S = "787cdd6e1d4fdf9a0d9f965eb85725232a9efcc12abfa1ef25a81e0983111d9000d494fc7d3201eb3bba327302727f7086147a755b4827030c7276536f425593ab2e9127a149e754de7ad77f8c2043267db49f8a35031d83f13d140d5df4d424b47454041a23b92ff6818e749d65d01fc50bebf69152f3f5fcb4873b1036219e22b1e74f8368c8c501ce65f2c929d90a8ec899630e802547a7ca6ef18ab3cb3eb4a691ee68aebeaf1b9c055ad12218039cf480cd8d294332c5e16ebbe6af11f8f4bf49f9b4ed2f511126ae780a3b784be8f4426abd17f8600074483f2af3b71a8964c6e0fa00049a1d940d34cc08839e0c59253d99e90d17871d489674695663626166d36ff91d8c2299a2f051eae2d60e8ed0bc3fac1e490b470c12f3d697f6fbfd880de2e90e9fcbd485fa3393198372fb01e4cec5c15917ecdd42e57c43ecf55a8c0ecbdcef1bce4e36d96d46b112570b53f82f3d2064b08ac78613670a28ea69d79c717eb1c294090dbd561fa6e504d09d265724e37a2dc6f445f6f528c9"

			msg = Cnv.FromHex(msghex)
			r = Sig.VerifyData(S, msg, rsakeyvalue, SigAlgorithm.Rsa_Pss_Sha512)
			Console.WriteLine("Sig.VerifyData() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

			Console.WriteLine("3. Test using independently-generated signature...")
			rsaprikey = "-----BEGIN RSA PRIVATE KEY-----" & "    MIICXgIBAAKBgQDH/zRrIL8qvdyGlYFRnddU7NjXY3u6P2YCrS/Gy4vkYhmUtfF0" & "    slIvY6RTF+kDEYCbZagsSjQYAFuqLZwAF5gNBPzDMfhZFmjY42Da9MGIg/0ePpye" & "    zJWXO7FN0DxQ1Jw8iHzvon4bVT++VswAQe2158w/Q5cwOF2h89E/KMuwqQIDAQAB" & "    AoGBAIVP2vwRzvvJpQbc/1+NDC0i14PzX1UNz4y3LqKfqXcp4Q1cnj+AYgIOtElj" & "    JcIS15w+DfS/3aumCXQNhPAWyhWtEiLNifOGvIK31G7Xb1QPnrfRueEnE5PEG5XZ" & "    zAC9cnxNoyun/6NcC4oo0qETj4obTNNStI30uo+bEtJ6g9ZVAkEA9Fa/acw+DpN1" & "    IxRo4ZIfrikqjCwZ+RO1aXKmndXv3s8dzWQvRgr72m31kZol1UmnpoMdPnps8Qgi" & "    dnNU6B2gMwJBANGKs5B/IDB5LwIJZteCrFsltcC3cdzxmcRTKfCq6m4wx2oDWTXF" & "    ZaygFtA+CXrrGxz0B0Jte3eGUpvsyasXn7MCQQDyyL+qAKgpE5xxHvaYLPoNtBny" & "    7l9gf5TjEmk8rDeMzYBvdf0DPCbFBD3eT60IIgfUDLQiQMO/PLYBvNfBTK7BAkAN" & "    ZmBLSkXls6o06CMCfyHEhmnUFCcc6PpbWrIg6N0rBMWL2wD2dlQlMOukj4MNsEFA" & "    nb5lGhk+MIHR5NeUsGMPAkEAuYLHs1uMpunnWbWf7XzsRPfs4mZE1VfZefqle2v6" & "    L9mZsntuuiwZ/9tegrNgIMrWovHy33K9xcu9g9KIeojrag==" & "    -----END RSA PRIVATE KEY-----"
			S = "445488de220010752634cf01df16172c22260abcb9dc26eccbe046a38e01e2fcb098266e39e337d99bb6ce33c7c4c8334e5f19d81ef341a1e3baf14e7d0a0e1eb67ac08bd5b0b2860b214a22e5254562f743bcf66d19c9dd05e4030f5aeb07e04d016973bf639c339f600ff7be39c27b3841728726e6c22b18fe69265a701d6b"
			msg = New Byte() {&H61, &H62, &H63}
			' "abc"
			' Extract RSA public key from private
			Dim pubkeystr As String = Rsa.PublicKeyFromPrivate(Rsa.ReadPrivateKey(rsaprikey, "")).ToString()
			' Verify signature using public key
			r = Sig.VerifyData(S, msg, pubkeystr, SigAlgorithm.Rsa_Pss_Sha1)
			Console.WriteLine("Sig.VerifyData() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

			' Same again but verify against message digest
			Console.WriteLine("4. Same again but verify against message digest...")
			digest = Hash.BytesFromBytes(msg, HashAlgorithm.Sha1)
			Console.WriteLine("SHA1('{0}')={1}", System.Text.Encoding.[Default].GetString(msg), Cnv.ToHex(digest))
			r = Sig.VerifyDigest(S, digest, pubkeystr, SigAlgorithm.Rsa_Pss_Sha1)
			Console.WriteLine("Sig.VerifyDigest() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

		End Sub

		Private Shared Sub test_SIG_SignData_PSS()
			Console.WriteLine(vbLf & "SIGN DATA USING RSA-PSS:")

			' Demonstrates:
'             * Sig.SignData() using SigAlgorithm.Rsa_Pss_Sha256
'             * Sig.SignData() using Sig.SigOptions.PssSaltLenZero and Sig.SigOptions.Mgf1Sha1
'             * Sig.VerifyData()
'             * Sig.SignFile() using SigAlgorithm.Rsa_Pss_Sha512 and Sig.SigOptions.Mgf1Sha1
'             * Sig.VerifyFile()
'             


			Dim msg As Byte()
			Dim sigval As String, oksig As String
			Dim r As Integer

			Dim certFile As String = "AliceRSAPssSignByCarl.cer"
			Dim priKeyFile As String = "AliceRSAPSS.p8e"
			' pkcs8-encrypted 
			Dim mypassword As String = "password"

			' Input data = three-char ASCII string "abc"
			msg = System.Text.Encoding.[Default].GetBytes("abc")
			Console.WriteLine("MSG: {0}", Cnv.ToHex(msg))

			Console.WriteLine("Sign using RSA-PSS-SHA256 (different each time)")
			' This will be different each time
			sigval = Sig.SignData(msg, priKeyFile, mypassword, SigAlgorithm.Rsa_Pss_Sha256)
			Console.WriteLine("SIG: {0}", sigval)
			If sigval.Length = 0 Then
				disp_error()
			End If
			Debug.Assert(sigval.Length > 0)

			Console.WriteLine("Verify using X.509 certificate")
			' We must specify the signature algorithm used to sign it
			r = Sig.VerifyData(sigval, msg, certFile, SigAlgorithm.Rsa_Pss_Sha256)
			Console.WriteLine("Sig.VerifyData() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

			Console.WriteLine("Sign using RSA-PSS-SHA256 with zero-length salt (so signature is deterministic) and MGF1-with-SHA1 (just to be awkward)")
			' (This should be the same each time)
			sigval = Sig.SignData(msg, priKeyFile, mypassword, SigAlgorithm.Rsa_Pss_Sha256, Sig.SigOptions.PssSaltLenZero Or Sig.SigOptions.Mgf1Sha1, 0)
			Console.WriteLine("SIG: {0}", sigval)
			Debug.Assert(sigval.Length > 0)
			' Known result
			oksig = "e0eb1ocnqg9raQBcjPYhjI5+l23P8aNWUHSPgaW5mUlfh2py1IjD1c/TDGQoIpQisbaBq0yDB4DSSpW0p9RqeQK33bELI3KM6A83q/5+J76WY/ZCPDSGJRTtovV4jiRSvoqPqcL/bJcQv1j9pqH2tTQmdSgArCYAntnLpBLYR2bbm3Q/1hnrs1T8CttYje+qSPvFAmShxSS8ryIm5POj6p2aXXtdDqo47B46nYeHAVArPUT1CKEXMelZWItlTWEjzqnofLO+nquYIpb7gNBExSfkwxTbBHa88UPu35eEe0AfLaxqEudi7YCAZ6/8cBC4MUXlx8Th6PQ5kPKN+i+ibw=="
			Debug.Assert(sigval = oksig)

			Console.WriteLine("Verify using X.509 certificate")
			' We must specify the signature algorithm used to sign it and must specify MGF1-with-SHA1, since we used that option
			' (however, salt length can be detected automatically from signature value)
			r = Sig.VerifyData(sigval, msg, certFile, SigAlgorithm.Rsa_Pss_Sha256, Sig.VerifyOpts.Mgf1Sha1)
			Console.WriteLine("Sig.VerifyData() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

			Console.WriteLine(vbLf & "Sign a file using RSA-PSS-SHA512 with salt length set to the maximum possible; output encoded in hex")
			Dim dataFile As String = "excontent.txt"
			Console.WriteLine("FILE: {0}", dataFile)
			' (This will be different each time)
			' Set the salt length to be the maximum possible and output signature in hex encoding
			sigval = Sig.SignFile(dataFile, priKeyFile, mypassword, SigAlgorithm.Rsa_Pss_Sha512, Sig.SigOptions.PssSaltLenMax, Sig.Encoding.Base16)
			Console.WriteLine("SIG: {0}", sigval)
			If sigval.Length = 0 Then
				disp_error()
			End If
			Debug.Assert(sigval.Length > 0)

			Console.WriteLine("Verify using X.509 certificate")
			' We must specify the signature algorithm used to sign it (but salt length is found automatically)
			r = Sig.VerifyFile(sigval, dataFile, certFile, SigAlgorithm.Rsa_Pss_Sha512)
			Console.WriteLine("Sig.VerifyFile() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

		End Sub

		Private Shared Sub test_SIG_SignData_ECDSA()
			Console.WriteLine(vbLf & "SIGN DATA USING ECDSA:")

			' Demonstrates:
'             * Sig.SignData() using SigAlgorithm.Ecdsa_Sha256
'             * Sig.SignData() using Sig.SigOptions.UseDeterministic and Sig.SigOptions.Asn1DERStructure
'             * Sig.VerifyData()
'             


			Dim msg As Byte()
			Dim sigval As String, oksig As String
			Dim r As Integer

			Dim certFile As String = "User_ECC_P256.cer"
			Dim priKeyFile As String = "User_ECC_P256.p8e"
			' pkcs8-encrypted 
			Dim mypassword As String = "password"

			' Input data = three-char ASCII string "abc"
			msg = System.Text.Encoding.[Default].GetBytes("abc")
			Console.WriteLine("MSG: {0}", Cnv.ToHex(msg))

			Console.WriteLine("Sign using PKI_SIG_ECDSA_SHA256 (different each time)")
			' This will be different each time
			sigval = Sig.SignData(msg, priKeyFile, mypassword, SigAlgorithm.Ecdsa_Sha256)
			Console.WriteLine("SIG: {0}", sigval)
			Debug.Assert(sigval.Length > 0)

			Console.WriteLine("Verify using X.509 certificate")
			' We must specify the signature algorithm used to sign it
			r = Sig.VerifyData(sigval, msg, certFile, SigAlgorithm.Ecdsa_Sha256)
			Console.WriteLine("Sig.VerifyData() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)

			Console.WriteLine("Sign using PKI_SIG_ECDSA_SHA256 with deterministic digital signature generation procedure of [RFC6979] ")
			Console.WriteLine("--output as BitCoin DER-encoded ASN.1 structure encoded in hex")
			' This should be the same each time
			sigval = Sig.SignData(msg, priKeyFile, mypassword, SigAlgorithm.Ecdsa_Sha256, Sig.SigOptions.UseDeterministic Or Sig.SigOptions.Asn1DERStructure, Sig.Encoding.Base16)
			Console.WriteLine("SIG: {0}", sigval)
			Debug.Assert(sigval.Length > 0)

			' Known result
			oksig = "304402202f088efe451adba26ccafe507c3db0083f14e6c7fc822970dbf73a8e30de5bc702203e8c02b4e4310aff7d1e990dc50fa633c396bebd8e1b3f7daa599e9cd8d89a74"
			Console.WriteLine("OK : {0}", oksig)

			Console.WriteLine("Verify using X.509 certificate")
			' We must specify the signature algorithm used to sign it (everything else is detected automatically)
			r = Sig.VerifyData(sigval, msg, certFile, SigAlgorithm.Ecdsa_Sha256)
			Console.WriteLine("Sig.VerifyData() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r)
		End Sub

		Private Shared Sub test_RSA_EncryptDecrypt()
			Console.WriteLine(vbLf & "ENCRYPT/DECRYPT SHORT MESSAGE USING RSA:")

			' Demonstrates:
'             * Rsa.Encrypt() using Rsa.EME.OAEP with default SHA-1
'             * Rsa.Encrypt() using Rsa.EME.OAEP and Rsa.HashAlg.Sha256
'             * Rsa.Encrypt() using default PKCS#1v1_5
'             * Rsa.Decrypt()
'             


			Dim encHex As String, msgHex As String, correctHex As String
			Dim encmsg As Byte(), msg As Byte(), outmsg As Byte()

			' RSA key file 1024-bit from pkcs-1v2-1-vec
			Dim priKeyFile As String = "rsa-oaep-1.p8"
			' PKCS#8 unencrypted PrivateKeyInfo
			Dim pubKeyFile As String = "rsa-oaep-1.pub"
			' PKCS#1 RSAPublicKey
			' RSAES-OAEP Encryption Example 1.1 from `oaep-vect.txt` in `pkcs-1v2-1-vec.zip`
			Console.WriteLine("1. Decrypt RSAES-OAEP Encryption Example 1.1 from `oaep-vect.txt` in `pkcs-1v2-1-vec.zip`")
			' Cipher value from RSA-OAEP test vector
			encHex = "354fe67b4a126d5d35fe36c777791a3f7ba13def484e2d3908aff722fad468fb21696de95d0be911c2d3174f8afcc201035f7b6d8e69402de5451618c21a535fa9d7bfc5b8dd9fc243f8cf927db31322d6e881eaa91a996170e657a05a266426d98c88003f8477c1227094a0d9fa1e8c4024309ce1ecccb5210035d47ac72e8a"
			msg = Rsa.Decrypt(Cnv.FromHex(encHex), priKeyFile, "", Rsa.EME.OAEP, 0, 0)
			msgHex = Cnv.ToHex(msg)
			Console.WriteLine("msg={0}", msgHex)

			' Known result from test vector
			correctHex = "6628194e12073db03ba94cda9ef9532397d50dba79b987004afefe34"
			Console.WriteLine("OK ={0}", correctHex)
			Debug.Assert([String].Equals(correctHex, msgHex, StringComparison.OrdinalIgnoreCase))

			Console.WriteLine("2a. Encrypt using RSA-OAEP with SHA-256 (different each time)")
			Console.WriteLine("INPUT : {0}", Cnv.ToHex(msg))
			encmsg = Rsa.Encrypt(msg, pubKeyFile, Rsa.EME.OAEP, Rsa.HashAlg.Sha256, 0)
			Console.WriteLine("OUTPUT: {0}", Cnv.ToHex(encmsg))
			Console.WriteLine("2b. Decrypt")
			' Note we must we must specify the hash function used in RSA-OAEP
			outmsg = Rsa.Decrypt(encmsg, priKeyFile, "", Rsa.EME.OAEP, Rsa.HashAlg.Sha256, 0)
			Console.WriteLine("OUTPUT: {0}", Cnv.ToHex(outmsg))
			Debug.Assert([String].Equals(correctHex, Cnv.ToHex(outmsg), StringComparison.OrdinalIgnoreCase))

			Console.WriteLine("3a. Encrypt using default PKCS#1v1_5")
			msg = Cnv.FromHex("616263")
			' "abc"
			Console.WriteLine("INPUT : {0}", Cnv.ToHex(msg))
			encmsg = Rsa.Encrypt(msg, pubKeyFile)
			Console.WriteLine("OUTPUT: {0}", Cnv.ToHex(encmsg))
			Console.WriteLine("3b. Decrypt")
			outmsg = Rsa.Decrypt(encmsg, priKeyFile, "")
			Console.WriteLine("OUTPUT: {0}", Cnv.ToHex(outmsg))
			Debug.Assert([String].Equals(Cnv.ToHex(msg), Cnv.ToHex(outmsg), StringComparison.OrdinalIgnoreCase))

		End Sub
		Private Shared Sub test_RSA_ToXMLStringEx()
			Console.WriteLine(vbLf & "Create an XML string representation of an RSA internal key string with 'ds:' namespace prefix...")

			' Demonstrates:
'             * Rsa.ReadPublicKey()
'             * Rsa.ToXMLString() using prefix "ds:"
'             


			Dim keyFile As String = "AliceRSAPssSignByCarl.cer"
			' Public key inside an X.509 certificate
			Console.WriteLine("FILE: {0}", keyFile)
			Dim priKey As String = Rsa.ReadPublicKey(keyFile).ToString()
			' Create XML string with "ds:" namespace prefix.
			Dim xmlKey As String = Rsa.ToXMLString(priKey, "ds", 0)
			' You'd insert this inside an <ds:KeyInfo><ds:KeyValue> element
			Console.WriteLine("XML:" & vbLf & "{0}", xmlKey)
			Debug.Assert(xmlKey.Length > 0)
		End Sub

		Private Shared Sub test_CMS_MakeSigData_PSS()
			Console.WriteLine(vbLf & "Create a signed-data object using RSA-PSS...")

			' Demonstrates:
'             * Rsa.ReadPrivateKey()
'             * Cms.MakeSigData() using Cms.SigAlg.Rsa_Pss_Sha224
'             * Cms.QuerySigData()
'             * Cms.VerifySigData()
'             * Cms.ReadSigDataToString()
'             


			Dim outFile As String = "SignedData_PSS.p7s"
			Dim inFile As String = "excontent.txt"
			Dim certFile As String = "User_RSA_2048.cer"
			Dim priKeyFile As String = "User_RSA_2048.p8e"
			Dim r As Integer
			Dim s As String, query As String
			Dim sbPriKey As StringBuilder = Rsa.ReadPrivateKey(priKeyFile, "password")
			Debug.Assert(sbPriKey.Length > 0, "Failed to read private key file")

			r = Cms.MakeSigData(outFile, inFile, certFile, sbPriKey.ToString(), Cms.SigAlg.Rsa_Pss_Sha224, 0)
			Debug.Assert(0 = r, "Cms.MakeSigData failed")
			Console.WriteLine("Created '{0}'", outFile)
			Console.WriteLine("SIGNED-DATA:" & vbLf & "{0}", Asn1.TextDumpToString(outFile, 0))
			' Query the signed-data object
			query = "digestAlgorithm"
			s = Cms.QuerySigData(outFile, query)
			Console.WriteLine("Cms.QuerySigData('{0}')={1}", query, s)
			query = "signatureAlgorithm"
			s = Cms.QuerySigData(outFile, query)
			Console.WriteLine("Cms.QuerySigData('{0}')={1}", query, s)
			query = "pssParams"
			s = Cms.QuerySigData(outFile, query)
			Console.WriteLine("Cms.QuerySigData('{0}')={1}", query, s)
			' Verify the signed-data
			r = Cms.VerifySigData(outFile)
			Console.WriteLine("Cms.VerifySigData() returns {0} (expecting 0)", r)
			Debug.Assert(r = 0, "Cms.VerifySigData failed")
			' Read the signed data
			s = Cms.ReadSigDataToString(outFile)
			Console.WriteLine("signed-data='{0}'", s)

		End Sub

		Private Shared Sub test_MakeSignedEnveloped_PSS_OAEP()
			Console.WriteLine(vbLf & "Create an enveloped-data object using RSA-OAEP" & vbLf & " containing a signed-data object signed using RSA-PSS-SHA256...")
			' Wrap a signed-data object directly inside an enveloped-data object (no S/MIME wrapping).
			' -- the same technique used by the latest German Health specifications (current in 2018).

			' Demonstrates:
'             * Rsa.ReadPrivateKey()
'             * Cms.MakeSigDataFromString() using Cms.SigAlg.Rsa_Pss_Sha256
'             * Cms.MakeEnvData() using Cms.KeyEncrAlgorithm.Rsa_Oaep and HashAlgorithm.Sha256
'             * Cms.ReadEnvDataToFile()
'             * Cms.ReadSigDataToString()
'             * Cms.VerifySigData()
'             * Cms.QueryEnvData()
'             * Cms.QuerySigData()
'             * Wipe.File()
'             * Wipe.String()
'             


			Dim r As Integer
			Dim s As String, query As String
			Dim msg As String = "Hi Bob, this is a secret message from Alice."
			Dim tempFile As String = "intermediate.tmp"
			Dim envDataFile As String = "ToBobSignedByAlice.p7m"
			Dim sbPriKey As StringBuilder
			' 1.1 Alice signs the message, saved as a temp intermediate file
			' -- Uses RSA-PSS with SHA-256
			sbPriKey = Rsa.ReadPrivateKey("AliceRSAPSS.p8e", "password")
			r = Cms.MakeSigDataFromString(tempFile, msg, "AliceRSAPssSignByCarl.cer;CarlRSAPssSelf.cer", sbPriKey.ToString(), Cms.SigAlg.Rsa_Pss_Sha256, Cms.SigDataOptions.IncludeAttributes Or Cms.SigDataOptions.AddSignTime)
			Console.WriteLine("Cms.MakeSigDataFromString() returns {0} (expecting 0)", r)
			Debug.Assert(0 = r)

			' 1.2 The message is encrypted in an enveloped-data file using Bob's public key from his X.509 certificate
			' -- uses RSA-OAEP + SHA-256 + AES-256 content encryption
			r = Cms.MakeEnvData(envDataFile, tempFile, "BobRSAPssSignByCarl.cer", CipherAlgorithm.Aes256, Cms.KeyEncrAlgorithm.Rsa_Oaep, HashAlgorithm.Sha256, _
				Cms.EnvDataOptions.None)
			Console.WriteLine("Cms.MakeEnvData() returns {0} (expecting 1)", r)
			Debug.Assert(r > 0)
			' Historical anomaly - returns number or recipients, not zero
			' 1.3 Delete the intermediate file and private key string
			Wipe.File(tempFile)
			Wipe.[String](sbPriKey)

			' Show the ASN (optional - if you understand ASN.1 encoding)
			'Console.WriteLine(Asn1.TextDumpToString(envDataFile, 0));

			' Query some details in the enveloped-data file
			Console.WriteLine("Query some details in the enveloped-data file...")
			query = "contentEncryptionAlgorithm"
			s = Cms.QueryEnvData(envDataFile, query)
			Console.WriteLine("{0}={1}", query, s)
			query = "keyEncryptionAlgorithm"
			s = Cms.QueryEnvData(envDataFile, query)
			Console.WriteLine("{0}={1}", query, s)
			query = "oaepParams"
			s = Cms.QueryEnvData(envDataFile, query)
			Console.WriteLine("{0}={1}", query, s)

			' Now send the encrypted file to Bob
			Console.WriteLine(vbLf & "Bob decrypts the file and verifies the signed-data...")
			Dim sigDataFile As String = "FromAlice.p7s"

			' 2.1 Bob decrypts the outer enveloped-data object using his own private key
			sbPriKey = Rsa.ReadPrivateKey("BobRSAPSS.p8e", "password")
			r = Cms.ReadEnvDataToFile(sigDataFile, envDataFile, "BobRSAPssSignByCarl.cer", sbPriKey.ToString())
			Console.WriteLine("Cms.ReadEnvDataToFile() returns {0} (expecting 0)", r)
			Debug.Assert(0 = r, "Cannot decrypt enveloped-data object")

			' 2.2a (optional) Check we have a proper signed-data object
			Console.WriteLine("Query some details in the signed-data file...")
			query = "signatureAlgorithm"
			s = Cms.QuerySigData(sigDataFile, query)
			Console.WriteLine("{0}={1}", query, s)
			query = "digestAlgorithm"
			s = Cms.QuerySigData(sigDataFile, query)
			Console.WriteLine("{0}={1}", query, s)
			query = "pssParams"
			s = Cms.QuerySigData(sigDataFile, query)
			Console.WriteLine("{0}={1}", query, s)

			' 2.2b Verify the signature
			r = Cms.VerifySigData(sigDataFile)
			Console.WriteLine("Cms.VerifySigData() returns {0} (expecting 0)", r)
			Debug.Assert(0 = r, "Cms.VerifysigData failed")

			' 2.3 Extract the signed message
			s = Cms.ReadSigDataToString(sigDataFile)
			Console.WriteLine("Cms.ReadSigDataToString() returns string of length {0} (expecting +ve)", s.Length)
			Debug.Assert(s.Length > 0)
			Console.WriteLine("message='{0}'", s)

			' 2.4 Clean up
			Wipe.[String](sbPriKey)

		End Sub

		Private Shared Sub test_PFX_MakeFile_PSS()
			Console.WriteLine(vbLf & "Create a PFX file using RSA-PSS...")

			Dim pfxFile As String = "AliceRSAPSS.pfx"
			Dim keyFile As String = "AliceRSAPSS.p8e"
			Dim certFile As String = "AliceRSAPssSignByCarl.cer"
			Dim r As Integer
			Dim keyStr As String

			r = Pfx.MakeFile(pfxFile, certFile, keyFile, "password", Nothing, 0)
			Console.WriteLine("Pfx.MakeFile() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r, "Pfx.MakeFile() failed")
			' Test what sort of ASN.1 file we made (expecting "PKCS12 PFX")
			Console.WriteLine("Asn1.Type({0})={1}", pfxFile, Asn1.Type(pfxFile))

			' Read in the RSA private key stored in the PFX file
			keyStr = Rsa.ReadPrivateKey(pfxFile, "password").ToString()
			' Show we got something...
			Console.WriteLine("Key bits={0}", Rsa.KeyBits(keyStr))
		End Sub

		Private Shared Sub test_PFX_MakeFile_AES256()
			Console.WriteLine(vbLf & "Create a PFX file using AES256-SHA256...")

			Dim pfxFile As String = "bob-aes256.pfx"
			Dim plainKeyFile As String = "lamps-bob.p8.pem"
			Dim keyFile As String = "lamps-bob.p8e"
			Dim certFile As String = "lamps-bob.crt"
			Dim password As String = "password"
			' BAD!!
			Dim r As Integer
			Dim keyStr As String, certStr As String
			Dim isok As Boolean


			' Read in unencrypted key file then save as encrypted
			keyStr = Rsa.ReadPrivateKey(plainKeyFile, "").ToString()
			Debug.Assert(keyStr.Length > 0, "Rsa.ReadPrivateKey failed")
			r = Rsa.SaveEncKey(keyFile, keyStr, password, Rsa.PbeOptions.Pbe_Pbkdf2_aes128_CBC)
			Debug.Assert(r = 0, "Rsa.SaveEncKey failed")

			' Make a PFX file using AES256-SHA256
			r = Pfx.MakeFile(pfxFile, certFile, keyFile, password, "Bob's friendly AES256 ID", Pfx.Options.Aes256_Sha256)
			Console.WriteLine("Pfx.MakeFile() returns {0} (expecting 0)", r)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r, "Pfx.MakeFile() failed")
			' Test what sort of ASN.1 file we made (expecting "PKCS12 PFX")
			Console.WriteLine("Asn1.Type({0})={1}", pfxFile, Asn1.Type(pfxFile))
			' Verify the MAC in the PFX file
			isok = Pfx.SignatureIsValid(pfxFile, password)
			Console.WriteLine("Pfx.SignatureIsValid returns {0}", isok)
			Debug.Assert(isok, "Pfx.SignatureIsValid failed")

			' Dump the ASN.1 structure
			Console.WriteLine(Asn1.TextDumpToString(pfxFile))

			' Read in the RSA private key stored in the PFX file
			keyStr = Rsa.ReadPrivateKey(pfxFile, password).ToString()
			Debug.Assert(keyStr.Length > 0, "Rsa.ReadPrivateKey failed")
			' Show we got something...
			Console.WriteLine("Key bits={0}", Rsa.KeyBits(keyStr))

			' Read in the certificate stored in the PFX file
			certStr = X509.ReadCertStringFromPFX(pfxFile, password)
			Debug.Assert(certStr.Length > 0, "X509.ReadCertStringFromPFX failed")
			' Show we got something...
			Console.WriteLine("SHA1(new-cert)={0}", X509.CertThumb(certStr, HashAlgorithm.Sha1))
			Console.WriteLine("SHA1(origcert)={0}", X509.CertThumb(certFile, HashAlgorithm.Sha1))
			Console.WriteLine("subjectName: {0}", X509.QueryCert(certStr, "subjectName"))

		End Sub

		Private Shared Sub test_CIPHER_EncryptAEAD()
			Console.WriteLine(vbLf & "ENCRYPT USING AEAD...")

			Dim key As Byte(), iv As Byte(), pt As Byte(), aad As Byte()
			Dim ct As Byte(), dt As Byte()
			Dim okhex As String

			' GCM Test Case #03 (AES-128)
			Console.WriteLine("GCM Test Case #03 (AES-128)")
			key = Cnv.FromHex("feffe9928665731c6d6a8f9467308308")
			iv = Cnv.FromHex("cafebabefacedbaddecaf888")
			pt = Cnv.FromHex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255")
			okhex = "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f59854d5c2af327cd64a62cf35abd2ba6fab4"

			Console.WriteLine("KY={0}", Cnv.ToHex(key))
			Console.WriteLine("IV={0}", Cnv.ToHex(iv))
			Console.WriteLine("PT={0}", Cnv.ToHex(pt))

			ct = Cipher.EncryptAEAD(pt, key, iv, AeadAlgorithm.Aes_128_Gcm)
			Console.WriteLine("CT={0}", Cnv.ToHex(ct))
			Console.WriteLine("OK={0}", okhex)
			If ct.Length = 0 Then
				disp_error()
			End If
			Debug.Assert(ByteArraysEqual(ct, Cnv.FromHex(okhex)))

			Console.WriteLine("Decrypt...")
			dt = Cipher.DecryptAEAD(ct, key, iv, AeadAlgorithm.Aes_128_Gcm)
			Console.WriteLine("DT={0}", Cnv.ToHex(dt))
			Debug.Assert(ByteArraysEqual(dt, pt))

			Console.WriteLine("Same again but prepend IV to start of output")
			ct = Cipher.EncryptAEAD(pt, key, iv, Nothing, AeadAlgorithm.Aes_128_Gcm, Cipher.Opts.PrefixIV)
			Console.WriteLine("CT={0}", Cnv.ToHex(ct))
			Console.WriteLine("Decrypt...")
			dt = Cipher.DecryptAEAD(ct, key, iv, Nothing, AeadAlgorithm.Aes_128_Gcm, Cipher.Opts.PrefixIV)
			Console.WriteLine("DT={0}", Cnv.ToHex(dt))
			Debug.Assert(ByteArraysEqual(dt, pt))

			Console.WriteLine("GCM Test Case #16 (AES-256)")
			key = Cnv.FromHex("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308")
			iv = Cnv.FromHex("cafebabefacedbaddecaf888")
			pt = Cnv.FromHex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39")
			aad = Cnv.FromHex("feedfacedeadbeeffeedfacedeadbeefabaddad2")
			okhex = "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f66276fc6ece0f4e1768cddf8853bb2d551b"

			Console.WriteLine("KY={0}", Cnv.ToHex(key))
			Console.WriteLine("IV={0}", Cnv.ToHex(iv))
			Console.WriteLine("PT={0}", Cnv.ToHex(pt))
			Console.WriteLine("AD={0}", Cnv.ToHex(aad))

			ct = Cipher.EncryptAEAD(pt, key, iv, aad, AeadAlgorithm.Aes_256_Gcm, 0)
			Console.WriteLine("CT={0}", Cnv.ToHex(ct))
			Console.WriteLine("OK={0}", okhex)
			If ct.Length = 0 Then
				disp_error()
			End If
			Debug.Assert(ByteArraysEqual(ct, Cnv.FromHex(okhex)))
			Console.WriteLine("Decrypt...")
			dt = Cipher.DecryptAEAD(ct, key, iv, aad, AeadAlgorithm.Aes_256_Gcm, 0)
			Console.WriteLine("DT={0}", Cnv.ToHex(dt))
			Debug.Assert(ByteArraysEqual(dt, pt))

			Console.WriteLine("GCM Test Case #07 (AES-192)")
			key = Cnv.FromHex("000000000000000000000000000000000000000000000000")
			iv = Cnv.FromHex("000000000000000000000000")
			' Plaintext is the empty string - a zero-length byte array
			pt = Cnv.FromHex("")
			' IV is prepended to output
			okhex = "000000000000000000000000cd33b28ac773f74ba00ed1f312572435"

			Console.WriteLine("KY={0}", Cnv.ToHex(key))
			Console.WriteLine("IV={0}", Cnv.ToHex(iv))
			Console.WriteLine("PT={0}", Cnv.ToHex(pt))

			ct = Cipher.EncryptAEAD(pt, key, iv, Nothing, AeadAlgorithm.Aes_192_Gcm, Cipher.Opts.PrefixIV)
			Console.WriteLine("CT={0}", Cnv.ToHex(ct))
			Console.WriteLine("OK={0}", okhex)
			If ct.Length = 0 Then
				disp_error()
			End If
			Debug.Assert(ByteArraysEqual(ct, Cnv.FromHex(okhex)))
			Console.WriteLine("Decrypt...")
			dt = Cipher.DecryptAEAD(ct, key, iv, Nothing, AeadAlgorithm.Aes_256_Gcm, Cipher.Opts.PrefixIV)
			Console.WriteLine("DT={0}", Cnv.ToHex(dt))
			Debug.Assert(ByteArraysEqual(dt, pt))
		End Sub

		Private Shared Sub test_X509_ReadCertFromP7Chain()
			Console.WriteLine(vbLf & "READ CERTS AS BASE64 STRING FROM P7 CHAIN DATA...")

			Dim ncerts As Integer, i As Integer
			Dim certstr As String
			Dim s As String
			' Input is a P7 chain file as a string in PEM format
			' bob.p7b (contains 2 X.509 certs: BobRSA and CarlRSA)
			Dim p7str As String = "-----BEGIN PKCS7-----" & "MIIERQYJKoZIhvcNAQcCoIIENjCCBDICAQExADALBgkqhkiG9w0BBwGgggQaMIICJzCCAZCgAwIB" & "AgIQRjRrx4AAVrwR024uzV1x0DANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdDYXJsUlNBMB4X" & "DTk5MDkxOTAxMDkwMloXDTM5MTIzMTIzNTk1OVowETEPMA0GA1UEAxMGQm9iUlNBMIGfMA0GCSqG" & "SIb3DQEBAQUAA4GNADCBiQKBgQCp4WeYPznVX/Kgk0FepnmJhcg1XZqRW/sdAdoZcCYXD72lItA1" & "hW16mGYUQVzPt7cIOwnJkbgZaTdt+WUee9mpMySjfzu7r0YBhjY0MssHA1lS/IWLMQS4zBgIFEjm" & "Txz7XWDE4FwfU9N/U9hpAfEF+Hpw0b6Dxl84zxwsqmqn6wIDAQABo38wfTAMBgNVHRMBAf8EAjAA" & "MA4GA1UdDwEB/wQEAwIFIDAfBgNVHSMEGDAWgBTp4JAnrHggeprTTPJCN04irp44uzAdBgNVHQ4E" & "FgQU6PS4Z9izlqQq8xGqKdOVWoYWtCQwHQYDVR0RBBYwFIESQm9iUlNBQGV4YW1wbGUuY29tMA0G" & "CSqGSIb3DQEBBQUAA4GBAHuOZsXxED8QIEyIcat7QGshM/pKld6dDltrlCEFwPLhfirNnJOIh/uL" & "t359QWHh5NZt+eIEVWFFvGQnRMChvVl52R1kPCHWRbBdaDOS6qzxV+WBfZjmNZGjOd539OgcOync" & "f1EHl/M28FAK3Zvetl44ESv7V+qJba3JiNiPzyvTMIIB6zCCAVSgAwIBAgIQRjRrx4AAVrwR024u" & "n/JQIDANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdDYXJsUlNBMB4XDTk5MDgxODA3MDAwMFoX" & "DTM5MTIzMTIzNTk1OVowEjEQMA4GA1UEAxMHQ2FybFJTQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw" & "gYkCgYEA5Ev/GLgkV/R3/25ze5NxXLwzGpKSciPYQUbQzRE6BLOOr4KdvVEeF3rydiwrhjmnvdeN" & "GlPs5ADV6OyiNrHt4lDiMgmKP5+ZJY+4Tqu5fdWWZdoWoMW+Dq5EW+9e9Kcpy4LdrETpqpOUKQ74" & "GNbIV17ydsTyEWA4uRs8HZfJavECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E" & "BAMCAYYwHQYDVR0OBBYEFOngkCeseCB6mtNM8kI3TiKunji7MA0GCSqGSIb3DQEBBQUAA4GBALee" & "1ATT7Snk/4mJFS5M2wzwSA8yYe7EBOwSXS3/D2RZfgrD7Rj941ZAN6cHtfA4EmFQ7e/dP+MLuGGl" & "pJs85p6cVJq2ldbabDu1LUU1nUkBdvq5uTH5+WsSU6D1FGCbfco+8lNrsDdvreZ019v6WuoUQWNd" & "zb7IDsHaao1TNBgCMQA=" & "-----END PKCS7-----"
			' Check type of ASN.1 data
			s = Asn1.Type(p7str)
			Console.WriteLine("Data type is [{0}]", s)
			' Get count of certs in P7
			s = X509.ReadCertStringFromP7Chain(p7str, 0)
			Debug.Assert(s.Length > 0)
			' Count is returned as a string, e.g. "2", so convert to a number
			ncerts = Int32.Parse(s)
			Console.WriteLine("ncerts={0}", ncerts)
			' Read each cert in turn
			For i = 1 To ncerts
				certstr = X509.ReadCertStringFromP7Chain(p7str, i)
				Console.WriteLine("{0}", certstr)
				' Query the cert for subjectName
				s = X509.QueryCert(certstr, "subjectName")
				Console.WriteLine("subjectName='{0}'", s)
			Next
		End Sub

		Private Shared Sub test_X509_ReadCertFromPFX()
			Console.WriteLine(vbLf & "READ CERT AS BASE64 STRING FROM PFX...")

			Dim certstr As String
			Dim s As String
			' Input is a PFX file as a string in PEM format
			' bob.pfx (password="password")
			Dim pfxStr As String = "-----BEGIN PKCS12-----" & "MIIGhAIBAzCCBkoGCSqGSIb3DQEHAaCCBjsEggY3MIIGMzCCAv8GCSqGSIb3DQEHBqCCAvAwggLsAgEAMIIC5QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIawU" & "AVTFvAiECAggAgIICuNwEuFcRnZamZyMyIn+vH+wC5BVUtZAWNrlIqToezF7cYqt/18+HXB/46nllz+qUD3Dv9rS78MnPeAM47afFRTricHsiOpE+2eXf32lxduoF5+" & "CLS3S7TAhRUMp2Fh18LlukzK9lY67BGfU9Y3yCukTmwVXqe49dkj8y9JjVJhXnoc2c7eOk3o5RjXHFsAMHwirqdsESHstrDZYLMVGw5HnAamY7zQd8WUpIweAFaEDLJ" & "fyzqY1/LTL/txvZ9VQ/B/36HKyEpoIvuH6iOCBkebpJwWSkkffuVFbUfMLguMztL/sf+jE2NiuljSBJ9pTNsZziZWERb6CxZH0a2xkkBTciXM5Dl5efWL0GmBg+aJSI" & "yh+Gw5W8Q7gmnH6H9myszvW9uYv/epwCbIpHd0dRHPbL3fR4KGhFexq24tAG86tDqPKb6H6n0lSA+Oq46SwZ00xIFpVcFaO/8yVqf6+JRDGoZ55aAZF6OCi7R1GvI+6" & "pzz37pvP7SWfqVSuXCTNQq9uKw97SH5YftQ9hkELQ4vHCjFh4UJSBUCZgDtqR1uB/+44H5UpP8KvbETaOFJszMxsqXBMqc1uEODSNg+EHEx+yg7Bx1CcNrm+6rtThC4" & "9+ow18HDMxbn3lAw1ooblANvSzR4YTt68N/4dtwROOdXjwKzyg03qWK2sJaiH5LzbB5MMmrdAChb9dLoRKBN2LREob7KRKEs6v51IW1yq4UCwSmpP+RbchZwIoKVXx/" & "MYKjVqzGfZAgBRpXEq/KH/8R+ttFPKdab2GAEjd7hIOmetp5einQmK4C7JYE6Uyabf1IImtVhBw2dGU3GiM2zSIGqCx3bmYETZheMTAV9MMVUYe8gQeEpbXM4GAnwX0" & "wpS0aYapzGeA/62X2nFh21eRHVzUcf0miXVvyOy6a1vj6O6N5F1jVaCV3jCCAywGCSqGSIb3DQEHAaCCAx0EggMZMIIDFTCCAxEGCyqGSIb3DQEMCgECoIICpjCCAqI" & "wHAYKKoZIhvcNAQwBAzAOBAjw/dx4SlLcWwICCAAEggKALm91I8gYuPpRTCSn5pN4OQBLbI6jSW+9FGeNYvOy/+Pt3Oq0i15ZXZZez7dP8rdb0tmTCSZwVPIwtJRKxY" & "UNaTppUTWZhXhnmeTMtSZpFuKmo6UhW8lGUcg45sO5UKUtdH0/UgewaSUfV4L06vp4j7Fugwbp666seJJ/9vQwMAxoqj0blxNNmASAcW7yj/lA2/p4KuGlnGkv4MSW5" & "ViH7T24VeFXTzyFFR7UR1Nw9Blr5jdr7b2rZSdTj0GeHZ/L3FksFWJocl8PEEL4ZdVscbvO+l7vtbeBz0y9TDr/HUwt2tfqXgjckVVoJhmsczJXrG5Ai+brKnGQ7R5u" & "IpIsqd9O6EpG68VMMGA5iSKsLYtibieqom8mRO00sFiQharxONEdveY+3O98nG6xzHlaBdNbxVo38Y+4LK6Gc81dUWYwss3ajdiJWe0+TYQjMPF72eWctcQAoTxITpd" & "/j6rD7EmvLVyPIR46L4w6Gb/uz5G1T1UiLoh9luM1nRKKICyo2XllZDNO0msaub7DH1xzJzEy2OT9cwChqYfKKeWEE2BWL699fmq5RMCbIQVtE2bJDP8obu9j6HLskC" & "iZcJm6nC7IKS1pQ2BA/JJVKxC8ADuLOAOdicWquDd8MWL5a9HpXd5TtUlfiRecTw8IRozTLaoDVlhaYNGPzwkjL9zZ+Up5Uy6HHXMDb0aD0fgvMqdAspB1+Xlt2RgP6" & "CnEH2hwQqGFoA8TtijeS+DtdMy8BxJ7g1fiEH0+4UISl1vymjPI1MJCI1VlFLvpjZvKHluwjgp1SHk3tFRJLJ8a/eApvmscKXSlxcYz+5Bv8dxPGdhO/KOLQS7XZ4a8" & "VSg977WS1jFYMCMGCSqGSIb3DQEJFTEWBBRj8EbS3XBC5R/cJqUR73yB6mItizAxBgkqhkiG9w0BCRQxJB4iAEIAbwBiACcAcwAgAGYAcgBpAGUAbgBkAGwAeQAgAEk" & "ARDAxMCEwCQYFKw4DAhoFAAQUaHSMUJ415FfKGv3cZpwloKDmqgYECAreM3EkHVjCAgIIAA==" & "-----END PKCS12-----"
			' Check type of ASN.1 data
			s = Asn1.Type(pfxStr)
			Console.WriteLine("Data type is [{0}]", s)
			' Read in cert as base64 string
			certstr = X509.ReadCertStringFromPFX(pfxStr, "password")
			' SECURITY!!
			Console.WriteLine("{0}", certstr)
			' Query the cert for subjectName
			s = X509.QueryCert(certstr, "subjectName")
			Console.WriteLine("subjectName='{0}'", s)
		End Sub


		' NEW IN [v12.2]

		Private Shared Sub test_Cms_Data_Bytes()
			Console.WriteLine(vbLf & "CMS SIGNED DATA USING RAW BYTES:")
			' We will create a signed-data object with "raw" content as a UTF-8-encoded XML document.

			Dim sigdataFile As String = "sigDataByAlice.p7m"
			Dim prikeyFile As String = "AlicePrivRSASign.p8e"
			Dim certFile As String = "AliceRSASignByCarl.cer"
			Dim sbPrivateKey As StringBuilder
			Dim r As Integer
			Dim query As String, s As String
			Dim contentArr As Byte()

			' Create an XML document as a string with lots of obscure characters
			' and a Byte-Order Mark (BOM).
			Dim xmlStr As String = "<?xml version=""1.0""?><doc>" & vbLf & "<name c='es'>Íñigo</name>" & vbLf & "<name c='fr'>Françoise</name>" & vbLf & "<name c='pl'>Błażej</name>" & vbLf & "<name c='cn'>大卫</name>" & vbLf & "</doc>"
			Console.OutputEncoding = System.Text.Encoding.UTF8
			Console.WriteLine("Raw XML (may not all display correctly in console):")
			Console.WriteLine("{0}", xmlStr)
			Console.WriteLine("XML as string is {0} chars", xmlStr.Length)

			' Convert this .NET Unicode string to UTF8 encoding
			Dim xmlArr As Byte() = System.Text.Encoding.UTF8.GetBytes(xmlStr)
			Console.WriteLine("XML as UTF-8 bytes is {0} bytes", xmlArr.Length)
			' Display in hex
			Console.WriteLine("UTF-8 in hex:")
			Console.WriteLine(Cnv.ToHex(xmlArr))

			' Now sign this using Alice's private key
			sbPrivateKey = Rsa.ReadPrivateKey(prikeyFile, "password")
			r = Cms.MakeSigDataFromBytes(sigdataFile, xmlArr, certFile, sbPrivateKey.ToString(), Cms.SigAlg.Rsa_Sha1, 0)
			Console.WriteLine("Cms.MakeSigDataFromBytes() returns {0} (expecting 0)", r)
			' Clean up private key
			Wipe.[String](sbPrivateKey)
			Debug.Assert(0 = r)
			Console.WriteLine("Created signed-data file '{0}'", sigdataFile)

			' Query the new signed-data object
			query = "HASeContent"
			s = Cms.QuerySigData(sigdataFile, query)
			Console.WriteLine("Cms.QuerySigData('{0}')={1}", query, s)

			' Extract the signed content to a new byte array
			contentArr = Cms.ReadSigDataToBytes(sigdataFile)
			Console.WriteLine("Content length is {0} bytes", contentArr.Length)

			' We know it's UTF-8-encoded so we can convert back to a .NET Unicode string
			Dim contentStr As String = System.Text.Encoding.UTF8.GetString(contentArr)
			Console.WriteLine("{0}", contentStr)
			Console.WriteLine("Content as string is {0} chars", contentStr.Length)

			' Extract and save UTF-8-encoded XML to file
			Dim xmlFile As String = "exampleUTF8.xml"
			r = Cms.ReadSigDataToFile(xmlFile, sigdataFile)
			Console.WriteLine("Cms.ReadSigDataToFile() returns {0} (expecting +ve)", r)
			Debug.Assert(r > 0)
			' NB returns # bytes in file, not zero
			Console.WriteLine("Extracted content to file '{0}'", xmlFile)

			Console.WriteLine(vbLf & "CMS ENVELOPED DATA USING RAW BYTES:")
			' Use the same XML data to make an enveloped data file for Bob, Alice and Carl
			' But first we'll make a PKCS#7 certificate chain file and use that to create the env-data
			Dim p7file As String = "bob_alice_carl.p7b"
			' No need for input file or private key (P7 cert chain is a "degenerate" signed-data file)
			r = Cms.MakeSigData(p7file, "", "BobRSASignByCarl.cer;AliceRSASignByCarl.cer;CarlRSASelf.cer", "", Cms.SigAlg.[Default], Cms.SigDataOptions.CertsOnly)
			Console.WriteLine("Cms.MakeSigData returns {0}", r)
			Debug.Assert(0 = r)
			Console.WriteLine("Created P7 chain file '{0}'", p7file)
			Console.WriteLine("ASN.1 type={0}", Asn1.Type(p7file))

			Dim envdataFile As String = "envDataForBobAliceCarl.p7m"
			'  Use AES-128 for content encryption and RSA-OAEP with SHA-256 for key transport
			' [New in v12.2] we can pass a P7 file instead of a list of certificates
			r = Cms.MakeEnvDataFromBytes(envdataFile, xmlArr, p7file, CipherAlgorithm.Aes128, Cms.KeyEncrAlgorithm.Rsa_Oaep, HashAlgorithm.Sha256, _
				0)
			Console.WriteLine("Cms.MakeEnvDataFromBytes returns {0} (= # recipients)", r)
			Debug.Assert(r > 1)
			Console.WriteLine("Created enveloped-data file '{0}'", envdataFile)

			' Any of Bob, Alice or Carl can decrypt the message - we'll use Bob's private key
			sbPrivateKey = Rsa.ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password")
			Dim arrMsg As Byte() = Cms.ReadEnvDataToBytes(envdataFile, "", sbPrivateKey.ToString())
			' Display in hex
			Console.WriteLine("Received message in hex:")
			Console.WriteLine(Cnv.ToHex(arrMsg))
		End Sub

		Private Shared Sub test_ReadJWK()
			Console.WriteLine(vbLf & "READ IN RSA KEY REPRESENTED AS JSON JWK:")
			' RSA public key as a JSON string
			' Ref: RFC 7517 JSON Web Key (JWK) Appendix A.1
			Dim json As String = "{""kty"":""RSA""," & """n"": ""0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx" & "4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs" & "tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2" & "QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI" & "SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb" & "w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw""," & """e"":""AQAB""," & """alg"":""RS256""," & """kid"":""2011-04-29""}"
			Console.WriteLine("JSON key={0}", json)
			Dim sbPublicKey As StringBuilder = Rsa.ReadPublicKey(json)
			Debug.Assert(sbPublicKey.Length > 0)
			' Display some key properties
			Console.WriteLine("RSA key size = {0} bits", Rsa.KeyBits(sbPublicKey.ToString()))
			Console.WriteLine("Key hash code = 0x{0,8:X}", Rsa.KeyHashCode(sbPublicKey.ToString()))
		End Sub

		' NEW IN [v12.3]

		Private Shared Sub test_Cipher_Encrypt_Prefix()
			Console.WriteLine(vbLf & "ENCRYPT WITH PREFIXED IV xmlenc#aes128-cbc:")
			Dim plain As String = "<encryptme>hello world</encryptme>"
			Dim ciphervalue As String
			Dim key As Byte() = Cnv.FromHex("6162636465666768696A6B6C6D6E6F70")
			Dim iv As Byte() = Rng.Bytes(Cipher.BlockBytes(CipherAlgorithm.Aes128))
			Dim pt As Byte() = Encoding.UTF8.GetBytes(plain)
			Console.WriteLine("PT='{0}'", plain)
			Console.WriteLine("PT={0}", Cnv.ToHex(pt))
			Console.WriteLine("KEY={0}", Cnv.ToHex(key))
			Console.WriteLine("IV={0}", Cnv.ToHex(iv))
			Dim ct As Byte() = Cipher.Encrypt(pt, key, iv, CipherAlgorithm.Aes128, Mode.CBC, Padding.Pkcs5, _
				Cipher.Opts.PrefixIV)
			Console.WriteLine("IV|CT={0}", Cnv.ToHex(ct))
			' Encode using base64
			ciphervalue = Cnv.ToBase64(ct)
			' NB different each time
			Console.WriteLine("<CipherValue>{0}</CipherValue>", ciphervalue)

			'------------------
			' PART 2 - decrypt
			Console.WriteLine("DECRYPTING...")
			ct = Cnv.FromBase64(ciphervalue)
			Console.WriteLine("IV|CT={0}", Cnv.ToHex(ct))
			Dim dt As Byte() = Cipher.Decrypt(ct, key, Nothing, CipherAlgorithm.Aes128, Mode.CBC, Padding.Pkcs5, _
				Cipher.Opts.PrefixIV)
			Console.WriteLine("DT={0}", Cnv.ToHex(dt))
			' Convert from bytes to string
			Console.WriteLine("DT='{0}'", Encoding.UTF8.GetString(dt))
		End Sub

		Private Shared Sub test_X509_MakeCert_EmptyDN()
			Console.WriteLine(vbLf & "MAKE CERT WITH EMPTY DN:")
			Dim certname As String = "AliceRSA-emptyDN.cer"
			Dim issuerCert As String = "CarlRSASelf.cer"
			Dim prikeyfile As String = "CarlPrivRSASign.p8e"
			Dim password As String = "password"
			Dim subjectPubKeyFile As String = "AlicePubRSA.pub"
			Dim dn As String = "$"
			' special flag for empty DN
			Dim extns As String = "iPAddress=192.168.15.1"
			' at least one field for subject alt name is required
			Dim keyUsage As X509.KeyUsageOptions = X509.KeyUsageOptions.DigitalSignature Or X509.KeyUsageOptions.NonRepudiation

			' Create a new certificate for Alice signed by Carl valid for 2 years signed using RSA-SHA-256
			' Subject's distinguished name is empty, Subject alternative name is automatically marked CRITICAL (denoted "[!]" in dump)
			Dim r As Integer = X509.MakeCert(certname, issuerCert, subjectPubKeyFile, prikeyfile, &H1001, 2, _
				dn, extns, keyUsage, password, SigAlgorithm.Rsa_Sha256, X509.CertOptions.AuthKeyId)
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r, "X509.MakeCert failed")
			Console.WriteLine("Created new X509 file '{0}'", certname)
			x509DumpFile(certname)

		End Sub
		Private Shared Sub test_X509_CertRequest_EmptyDN_extKeyUsage()
			Console.WriteLine(vbLf & "MAKE CERTIFICATE SIGNING REQUEST WITH EMPTY DN AND EXTENDED KEY USAGE:")
			Dim csrfile As String = "req_emptydn_extkeyusage.p10"
			Dim epkfile As String = "AlicePrivRSASign.p8e"
			Dim password As String = "password"
			Dim dn As String = "$"
			' special flag for empty DN
			' Use extensions parameter to add alt subject name and extended key usage flags
			Dim extns As String = "iPAddress=192.168.15.1;extKeyUsage=serverAuth,clientAuth,emailProtection,critical;"
			Dim r As Integer

			Dim certfile As String = "certfromcsr_emptydn_extkeyusage.cer"
			Dim issuerCert As String = "CarlRSASelf.cer"
			Dim issuerEpkfile As String = "CarlPrivRSASign.p8e"
			Dim issuerPassword As String = "password"
			Dim query As String
			Dim s As String

			' Create a CSR for Alice
			' Subject's distinguished name is empty, Subject alternative name is marked CRITICAL (denoted "[!]" in dump)
			r = X509.CertRequest(csrfile, epkfile, dn, extns, password, SigAlgorithm.Rsa_Sha256, _
				X509.CsrOptions.[Default])
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r, "X509.CertRequest failed")
			Console.WriteLine("Created certificate request '{0}'", csrfile)
			x509DumpFile(csrfile)

			' Now use this PKCS#10 CSR to create an end-user X.509 certificate for Alice signed by Carl valid for 4 years
			r = X509.MakeCert(certfile, issuerCert, csrfile, issuerEpkfile, &H10b, 4, _
				"", "", 0, issuerPassword, SigAlgorithm.Rsa_Sha256, X509.CertOptions.[Default])
			If r <> 0 Then
				disp_error(r)
			End If
			Debug.Assert(0 = r, "X509.MakeCert failed")
			Console.WriteLine("Created end-user X.509 certificate '{0}'", certfile)
			x509DumpFile(certfile)
			' Query the new certificate
			query = "subjectName"
			s = X509.QueryCert(certfile, query)
			Console.WriteLine("Query {0}='{1}'", query, s)
			query = "subjectAltName"
			s = X509.QueryCert(certfile, query)
			Console.WriteLine("Query {0}='{1}'", query, s)
			query = "extKeyUsageString"
			s = X509.QueryCert(certfile, query)
			Console.WriteLine("Query {0}='{1}'", query, s)
		End Sub

		Private Shared Sub test_X509_ReadCertStringFromPFX_3des()
			Console.WriteLine(vbLf & "READ IN CERT AS A STRING FROM PFX FILE USING 3DES ENCRYPTION:")

			' PFX file from draft-dkg-lamps-samples-02 with cert encrypted using "stronger" 3DES
			' Ref: IETF LAMPS WG https://gitlab.com/dkg/lamps-samples
			Dim pfxfile As String = "bob-lamps.p12"
			Dim password As String = "bob"
			Dim okcerthash As String = "6688f64f99744f7faf1059e2b49d1f5042bb2406"
			Console.WriteLine("FILE: {0}", pfxfile)
			' Extract cert as a base64 string
			Dim certstr As String = X509.ReadCertStringFromPFX(pfxfile, password)
			Debug.Assert(certstr.Length > 0, "X509.ReadCertstringFromPFX failed")
			Console.WriteLine("BASE64(CER)={0}...{1}", certstr.Substring(0, 30), certstr.Substring(certstr.Length - 20))
			' Check digest
			Dim digest As String = X509.CertThumb(certstr, HashAlgorithm.Sha1)
			Console.WriteLine("SHA-1(CER)={0}", digest)
			Console.WriteLine("OK        ={0}", okcerthash)
			Debug.Assert(digest = okcerthash, "Cert thumbprints do not match")
			' Get name of ASN.1 object
			Dim asn1type As String = Asn1.Type(certstr)
			Console.WriteLine("Asn1.Type(CER)={0}", asn1type)
		End Sub

		Private Shared Sub test_PFX_MakeFile_3DES()
			Console.WriteLine(vbLf & "CREATE A NEW PFX FILE USING 3DES TO ENCRYPT THE CERT:")

			Dim epkfile As String = "BobPrivRSAEncrypt.p8e"
			Dim certfile As String = "BobRSASignByCarl.cer"
			Dim pfxfile As String = "bob-3des.pfx"
			Dim password As String = "password"

			' Use StrongCert option to encrypt cert using "stronger" 3DES instead of weak default 40-bit RC2.
			Dim r As Integer = Pfx.MakeFile(pfxfile, certfile, epkfile, password, "Old Bob", Pfx.Options.StrongCert)

			Console.WriteLine("Pfx.MakeFile returns {0} (expecting 0)", r)
			Debug.Assert(0 = r, "Pfx.MakeFile failed")
			' Check type of file we just made
			Dim asn1type As String = Asn1.Type(pfxfile)
			Console.WriteLine("Asn1.Type={0}", asn1type)
			' Now dump the ASN.1
			' Note that "pkcs-12-pkcs-8ShroudedKeyBag" is encrypted with "pbeWithSHAAnd3-KeyTripleDES-CBC"
			' (see lines 89 and 95 (approx) of output)
			asn1DumpFile(pfxfile)

		End Sub

		Private Shared Sub test_Rng_Guid()
			Console.WriteLine(vbLf & "GENERATE RANDOM GUID (UUID) STRINGS:")
			Dim s As String
			For i As Integer = 0 To 4
				s = Rng.Guid()
				Console.WriteLine(s)
			Next

		End Sub

		' [v12.3.1]

		Private Shared Sub test_CMS_MakeSigData_signingcert()
			Console.WriteLine(vbLf & "MAKE CMS SIGNED DATA WITH SIGNING CERTIFICATE ATTRIBUTE:")
			Dim xmldata As String = "<a>Some sample content.</a>"
			Dim certfile As String = "alice-lamps.crt"
			Dim keyfile As String = "alice-lamps.p12"
			Dim password As String = "alice"
			Dim outfile As String = "signedbyalice-signingcert.p7m"

			' Read in private key to an ephemeral string
			Dim sbPrivateKey As StringBuilder = Rsa.ReadPrivateKey(keyfile, password)

			' Set options
			Dim sdopts As Cms.SigDataOptions = Cms.SigDataOptions.AltAlgId Or Cms.SigDataOptions.IncludeAttributes Or Cms.SigDataOptions.AddSignTime Or Cms.SigDataOptions.AddSigningCertificate

			' Create signed-data object using RSA-SHA256
			Dim r As Integer = Cms.MakeSigDataFromString(outfile, xmldata, certfile, sbPrivateKey.ToString(), Cms.SigAlg.Rsa_Sha256, sdopts)
			Console.WriteLine("Cms.MakeSigDataFromString returns {0} (expected 0)", r)
			asn1DumpFile(outfile)
		End Sub

		' [v20.0.0]

		Private Shared Sub test_CIPHER_EncryptHex()
			Console.WriteLine(vbLf & "ENCRYPT DATA USING HEX-ENCODED PARAMETERS:")
			' Aes128/CBC/OneAndZeroes
			Dim keyHex As String = "0123456789ABCDEFF0E1D2C3B4A59687"
			Dim ivHex As String = "FEDCBA9876543210FEDCBA9876543210"
			' "Now is the time for all good men to"
			Dim plainHex As String = "4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F"
			Dim okHex As String = "C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E1771D4CDA34FBFB7E74B321F9A2CF4EA61B"
			Dim cipherHex As String, chkHex As String
			Dim alg As CipherAlgorithm = CipherAlgorithm.Aes128
			Dim mode__1 As Mode = Mode.CBC
			Dim pad As Padding = Padding.OneAndZeroes
			Console.WriteLine("ALG: {0}/{1}/{2}", alg, mode__1, pad)
			Console.WriteLine("KY={0}", keyHex)
			Console.WriteLine("IV={0}", ivHex)
			Console.WriteLine("PT={0}", plainHex)
			' Aes128/CBC/OneAndZeroes
			cipherHex = Cipher.Encrypt(plainHex, keyHex, ivHex, alg, mode__1, pad)
			Console.WriteLine("CT={0}", cipherHex)
			Debug.Assert(cipherHex = okHex, "Cipher.Encrypt failed")
			chkHex = Cipher.Decrypt(cipherHex, keyHex, ivHex, alg, mode__1, pad)
			Console.WriteLine("P'={0}", chkHex)
			Debug.Assert(chkHex = plainHex, "Cipher.Decrypt failed")
			Console.WriteLine("P'='{0}'", Cnv.StringFromHex(chkHex))
			' Again using PrefixIV
			cipherHex = Cipher.Encrypt(plainHex, keyHex, ivHex, alg, mode__1, pad, _
				Cipher.Opts.PrefixIV)
			Console.WriteLine("CT(PrefixIV)={0}", cipherHex)
			Debug.Assert(cipherHex = ivHex & okHex, "Cipher.Encrypt(PrefixIV) failed")
			chkHex = Cipher.Decrypt(cipherHex, keyHex, ivHex, alg, mode__1, pad, _
				Cipher.Opts.PrefixIV)
			Console.WriteLine("P'={0}", chkHex)
			Debug.Assert(chkHex = plainHex, "Cipher.Decrypt failed")
			Console.WriteLine("P'='{0}'", Cnv.StringFromHex(chkHex))
		End Sub

		Private Shared Sub test_RSA_XML_withprefixes()
			Console.WriteLine(vbLf & "CONVERT BETWEEN RSA KEY VALUE AND XML KEY FORM:")
			' Read in private key from key file
			Dim keyfile As String = "AlicePrivRSASign.p8e"
			Console.WriteLine("FILE: {0}", keyfile)
			Dim prikeystr As String = Rsa.ReadPrivateKey(keyfile, "password").ToString()
			Console.WriteLine("Private key has {0} bits", Rsa.KeyBits(prikeystr))
			Console.WriteLine("Private key hashcode = 0x{0,8:X}", Rsa.KeyHashCode(prikeystr))

			' Convert to XML RSAKeyPair form with prefix
			Dim xmlstr As String = Rsa.ToXMLString(prikeystr, "ds:", 0)
			Console.WriteLine(xmlstr)

			' Now read in public key from this XML string (fixed to cope with prefixes in [v20.0])
			Dim pubkeystr As String = Rsa.ReadPublicKey(xmlstr).ToString()
			Console.WriteLine("Public key has {0} bits", Rsa.KeyBits(pubkeystr))
			Console.WriteLine("Public key hashcode = 0x{0,8:X}", Rsa.KeyHashCode(pubkeystr))

			Debug.Assert(Rsa.KeyHashCode(prikeystr) = Rsa.KeyHashCode(pubkeystr), "KeyHashCodes do not match")

		End Sub

		Private Shared Sub test_ECC_DHSharedSecret()
			Console.WriteLine(vbLf & "ECDH SHARED SECRET USING P-256:")
			' Ref: CAVS 14.1 ECC CDH Primitive (SP800 - 56A Section 5.7.1.2)
			' Test Information for "testecccdh" ecccdhtestvectors.zip
			' [P-256]
			Dim curve As Ecc.CurveName = Ecc.CurveName.P_256
			' Our private key is dUIT:
			Dim ourPrivateKeyHex As String = "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534"
			' Their public key as hex is "04" || QCAVSx || QCAVSy
			Dim theirPublicKeyHex As String = "04" & "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" & "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac"
			' Correct expected result
			Dim zzOK As String = "46FC62106420FF012E54A434FBDD2D25CCC5852060561E68040DD7778997BD7B"
			Dim ourPrivateKey As String = Ecc.ReadKeyByCurve(ourPrivateKeyHex, curve)
			Console.WriteLine("Curve = {0}", Ecc.QueryKey(ourPrivateKey, "curveName"))
			Console.WriteLine("Our private key has {0} bits", Ecc.QueryKey(ourPrivateKey, "keyBits"))
			Dim theirPublicKey As String = Ecc.ReadKeyByCurve(theirPublicKeyHex, curve)
			Console.WriteLine("Their public key has {0} bits", Ecc.QueryKey(theirPublicKey, "keyBits"))

			' Compute shared secret
			Dim zz As Byte() = Ecc.DHSharedSecret(ourPrivateKey, theirPublicKey)
			Console.WriteLine("ZZ={0}", Cnv.ToHex(zz))
			Debug.Assert(Cnv.ToHex(zz) = zzOK, "Ecc.DHSharedSecret failed")
		End Sub

		Private Shared Sub test_ECC_DHSharedSecret_25519()
			Console.WriteLine(vbLf & "ECDH SHARED SECRET USING X25519:")

			' Ref: RFC7748 Section 6.1
			' https://tools.ietf.org/html/rfc7748#section-6.1
			'
'            Test vector:
'            Alice's private key, a:
'                77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a
'            Alice's public key, X25519(a, 9):
'                8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a
'            Bob's private key, b:
'                5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb
'            Bob's public key, X25519(b, 9):
'                de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f
'            Their shared secret, K:
'                4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742
'            

			Dim curve As Ecc.CurveName = Ecc.CurveName.X25519

			Dim alicePrivateKeyHex As String = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"
			Dim alicePublicKeyHex As String = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"
			Dim bobPrivateKeyHex As String = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"
			Dim bobPublicKeyHex As String = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"
			' Correct expected result
			Dim zzOK As String = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"

			' 1. Alice's private + Bob's public
			' NB we *must* specify whether X25519 keys are public or private
			Dim ourPrivateKey As String = Ecc.ReadKeyByCurve(alicePrivateKeyHex, curve, Ecc.KeyType.PrivateKey)
			Console.WriteLine("Curve = {0}", Ecc.QueryKey(ourPrivateKey, "curveName"))
			Console.WriteLine("Our private key has {0} bits", Ecc.QueryKey(ourPrivateKey, "keyBits"))
			Dim theirPublicKey As String = Ecc.ReadKeyByCurve(bobPublicKeyHex, curve, Ecc.KeyType.PublicKey)
			Console.WriteLine("Their public key has {0} bits", Ecc.QueryKey(theirPublicKey, "keyBits"))
			' Compute shared secret
			Dim zz1 As Byte() = Ecc.DHSharedSecret(ourPrivateKey, theirPublicKey)
			Console.WriteLine("ZZ={0}", Cnv.ToHex(zz1))
			Debug.Assert(Cnv.ToHex(zz1).ToLower() = zzOK, "Ecc.DHSharedSecret failed")

			' 2. Bob's private + Alice's public
			ourPrivateKey = Ecc.ReadKeyByCurve(bobPrivateKeyHex, curve, Ecc.KeyType.PrivateKey)
			theirPublicKey = Ecc.ReadKeyByCurve(alicePublicKeyHex, curve, Ecc.KeyType.PublicKey)
			' Compute shared secret
			Dim zz2 As Byte() = Ecc.DHSharedSecret(ourPrivateKey, theirPublicKey)
			Console.WriteLine("ZZ={0}", Cnv.ToHex(zz2))
			Debug.Assert(Cnv.ToHex(zz2).ToLower() = zzOK, "Ecc.DHSharedSecret failed")
		End Sub

		Private Shared Sub test_SIG_SignData_Ed25519()
			Console.WriteLine(vbLf & "SIGN DATA USING Ed25519:")
			' Ref: [RFC8032] https://tools.ietf.org/html/rfc8032#section-7.1
			' -----TEST SHA(abc)

			' Read in private key from hex (NB need explicitly to identify as private key)
			Dim privateKey As String = Ecc.ReadKeyByCurve("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42", Ecc.CurveName.Ed25519, Ecc.KeyType.PrivateKey)
			Debug.Assert(privateKey.Length > 0, "Failed to read Ed25519 private key")
			Console.WriteLine("Private key has {0} bits", Ecc.QueryKey(privateKey, "keyBits"))
			Console.WriteLine("ALGORITHM: {0}", Ecc.QueryKey(privateKey, "curveName"))
			' Message is the 64-byte SHA-512 hash of "abc"
			Dim message As Byte() = Cnv.FromHex("ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f")
			Dim sig__1 As String = Sig.SignData(message, privateKey, "", SigAlgorithm.Ed25519, 0, Sig.Encoding.Base16)
			Console.WriteLine("SIGNATURE:" & vbLf & "{0}", sig__1)
			' Check against known correct result
			Debug.Assert(sig__1 = "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704")

			' Now verify using the public key
			Dim publicKey As String = Ecc.ReadKeyByCurve("ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", Ecc.CurveName.Ed25519, Ecc.KeyType.PublicKey)
			Debug.Assert(publicKey.Length > 0, "Failed to read Ed25519 public key")
			Console.WriteLine("Public key has {0} bits", Ecc.QueryKey(publicKey, "keyBits"))

			Dim r As Integer = Sig.VerifyData(sig__1, message, publicKey, SigAlgorithm.Ed25519)
			Console.WriteLine("Sig.VerifyData returns {0} (expected 0)", r)
			Debug.Assert(0 = r)
		End Sub

		Private Shared Sub test_X509_MakeCertSelf_Ed25519()
			Console.WriteLine(vbLf & "CREATE A SELF-SIGNED X.509 CERTIFICATE USING Ed25519:")
			' Ref: [RFC8410] https://tools.ietf.org/html/rfc8410

			Dim dn As String, extns As String, certname As String, prikeyfile As String, query As String
			Dim pubkeystr As String, issuercert As String
			Dim keyUsage As X509.KeyUsageOptions
			Dim r As Integer
			Dim s As String

			' 1. Create a new self-*signed* certificate using the Ed25519 key in RFC8410
			certname = "ietf-Ed25519-self.cer"
			prikeyfile = "edwards-ietf.p8"
			' No password
			dn = "CN=IETF Test Demo"
			extns = "notBefore=2016-01-01;notAfter=2040-12-31"
			'Digital Signature, Certificate Signing, [Off-line CRL Signing], CRL Signing
			keyUsage = X509.KeyUsageOptions.DigitalSignature Or X509.KeyUsageOptions.KeyCertSign Or X509.KeyUsageOptions.CrlSign
			r = X509.MakeCertSelf(certname, prikeyfile, &Hed25519, 0, dn, extns, _
				keyUsage, "", SigAlgorithm.Ed25519, X509.CertOptions.UTF8String)
			Console.WriteLine("X509.MakeCertSelf returns {0} (expecting 0)", r)
			Debug.Assert(0 = r, "X509.MakeCertSelf failed")
			' Dump details
			Console.WriteLine("FILE: {0}", certname)
			Console.WriteLine(X509.TextDumpToString(certname, 0))

			' Do a query on the cert
			query = "signatureAlgorithm"
			s = X509.QueryCert(certname, query)
			Console.WriteLine("{0}={1}", query, s)
			Debug.Assert(s.Length > 0, "X509.QueryCert failed")

			' 2. Now create a self-*issued* cert using Ed25519 to sign an X25519 public key
			' [RFC8410] 10.2. Example X25519 Certificate
			' NB This is self-*issued* in that the public key is for an X25519 key intended for ECDH,
			' but it is signed using an Ed25519 signature with a key also belonging to ones self.

			' Read in X25519 public key from its hex value
			' NB we *must* specify that it's a public key
			pubkeystr = Ecc.ReadKeyByCurve("8520F0098930A754748B7DDCB43EF75A0DBF3A0D26381AF4EBA4A98EAA9B4E6A", Ecc.CurveName.X25519, Ecc.KeyType.PublicKey)
			Debug.Assert(pubkeystr.Length > 0, "Ecc.ReadKeyByCurve failed")

			' Set cert parameters to closely duplicate the cert given in RFC8410 (almost!)
			dn = "CN=IETF Test Demo"
			extns = "notBefore=2016-08-01T12:19:24;notAfter=2040-12-31T23:59:59;keyUsage=noncritical;serialNumber=#x5601474A2A8DC330;" & "subjectKeyIdentifier=9B1F5EEDED043385E4F7BC623C5975B90BC8BB3B"
			keyUsage = X509.KeyUsageOptions.KeyAgreement

			issuercert = certname
			' Use the self-signed cert we made above to issue this new cert
			certname = "ietf-X25519-self-issued.cer"

			r = X509.MakeCert(certname, issuercert, pubkeystr, prikeyfile, 0, 0, _
				dn, extns, keyUsage, "", SigAlgorithm.Ed25519, X509.CertOptions.UTF8String)
			Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r)
			Debug.Assert(0 = r, "X509.MakeCert failed")
			' Dump details
			Console.WriteLine("FILE: {0}", certname)
			Console.WriteLine(X509.TextDumpToString(certname, 0))

			' Verify that this cert was signed by the above
			r = X509.VerifyCert(certname, issuercert)
			Console.WriteLine("X509.VerifyCert returns {0} (expecting 0 => verified OK)", r)
			Debug.Assert(0 = r, "X509.VerifyCert failed")

		End Sub

		Private Shared Sub test_CMS_MakeSigData_Ed25519()
			Console.WriteLine(vbLf & "CREATE A CMS SIGNED-DATA OBJECT USING Ed25519:")

			Dim outFile As String = "SignedData_Ed25519.p7m"
			Dim inFile As String = "excontent.txt"
			Dim certFile As String = "Ed25519-ietf-selfsigned.cer"
			' Self-signed cert created using private Ed25519 key in [RFC8410]
			Dim priKeyFile As String = "edwards-ietf-ex.p8"
			' No password, from [RFC8410]
			Dim r As Integer
			Dim s As String, query As String
			' Read in private key to internal key string
			Dim sbPriKey As StringBuilder = Ecc.ReadPrivateKey(priKeyFile, "")
			Debug.Assert(sbPriKey.Length > 0, "Failed to read private key file")

			' Create the signed-data object using Ed25519 with signed attributes incl Algorithm Protection
			Dim opts As Cms.SigDataOptions = Cms.SigDataOptions.IncludeAttributes Or Cms.SigDataOptions.AddAlgProtection
			r = Cms.MakeSigData(outFile, inFile, certFile, sbPriKey.ToString(), Cms.SigAlg.Ed25519, opts)
			Debug.Assert(0 = r, "Cms.MakeSigData failed")
			Console.WriteLine("Created '{0}'", outFile)
			Console.WriteLine("SIGNED-DATA:" & vbLf & "{0}", Asn1.TextDumpToString(outFile, 0))
			' Query the signed-data object
			query = "digestAlgorithm"
			s = Cms.QuerySigData(outFile, query)
			Console.WriteLine("Cms.QuerySigData('{0}')={1}", query, s)
			query = "signatureAlgorithm"
			s = Cms.QuerySigData(outFile, query)
			Console.WriteLine("Cms.QuerySigData('{0}')={1}", query, s)
			' Verify the signed-data
			r = Cms.VerifySigData(outFile)
			Console.WriteLine("Cms.VerifySigData() returns {0} (expecting 0)", r)
			Debug.Assert(r = 0, "Cms.VerifySigData failed")
			' Read the signed data
			s = Cms.ReadSigDataToString(outFile)
			Console.WriteLine("signed-data='{0}'", s)
			Debug.Assert(s.Length > 0, "Cms.ReadSigDataToString failed")

		End Sub


		Private Shared Sub test_RSA_SaveEncKey()
			Console.WriteLine(vbLf & "READ THEN SAVE AN ENCRYPTED RSA KEY:")
			Dim r As Integer
			Dim keyFile As String = "AlicePrivRSASign.p8e"
			Console.WriteLine("FILE: {0}", keyFile)
			' Read in private key to internal key string
			Dim sbPriKey As StringBuilder = Rsa.ReadPrivateKey(keyFile, "password")
			Debug.Assert(sbPriKey.Length > 0, "Rsa.ReadPrivateKey failed")
			Console.WriteLine("Private key size = {0} bits", Rsa.KeyBits(sbPriKey.ToString()))
			Console.WriteLine("KeyHashCode = {0,8:X}", Rsa.KeyHashCode(sbPriKey.ToString()))
			' Save with stronger encryption in PEM textual form
			keyFile = "AlicePrivRSASign-stronger.p8e"
			r = Rsa.SaveEncKey(keyFile, sbPriKey.ToString(), "password1", Rsa.PbeOptions.Pbe_Pbkdf2_aes192_CBC, "count=6666;prf=hmacWithSHA384", Rsa.Format.PEM)
			Debug.Assert(r = 0, "Rsa.SaveEncKey failed")
			Console.WriteLine("Saved new key file '{0}'", keyFile)

			' Read in ASN.1 dump of file we just created
			Dim s As String = Asn1.TextDumpToString(keyFile)
			' Console.WriteLine(s);

			' Expected to contain certain strings consistent with arguments above
			Debug.Assert(s.IndexOf("hmacWithSHA384") > 0)
			Debug.Assert(s.IndexOf("--6666") > 0)
			Debug.Assert(s.IndexOf("aes192-CBC") > 0)

			Dim sbPriKey1 As StringBuilder = Rsa.ReadPrivateKey(keyFile, "password1")
			Console.WriteLine("KeyHashCode = {0,8:X}", Rsa.KeyHashCode(sbPriKey1.ToString()))
			Debug.Assert(Rsa.KeyHashCode(sbPriKey.ToString()) = Rsa.KeyHashCode(sbPriKey1.ToString()), "KeyHashCodes do not match")
		End Sub

		Private Shared Sub test_General_FormatErrorMessage()
			Console.WriteLine(vbLf & "FORMAT ERROR MESSAGE:")
			Console.WriteLine(General.FormatErrorMessage(11))
			Console.WriteLine(General.FormatErrorMessage(11), "User message!")
			' Try and read missing file
			Dim s As String = Asn1.Type("missing.file")
			Console.WriteLine(General.FormatErrorMessage())
			Dim r As Integer = Cms.MakeEnvData("not-to-be-made.p7m", "missing.file", "", CipherAlgorithm.Aes128)
			Console.WriteLine(General.FormatErrorMessage(r))
			' Try to create a signed-data object but passing a missing cert file
			Dim sbPrivKey As StringBuilder = Rsa.ReadPrivateKey("AlicePrivRSASign.p8e", "password")
			r = Cms.MakeSigData("sigdata.p7m", "excontent.txt", "missing.file", sbPrivKey.ToString(), Cms.SigAlg.Rsa_Sha1, 0)
			Console.WriteLine(General.FormatErrorMessage(r, "Expected error!"))

		End Sub
		Private Shared Sub test_KDF_Bytes()
			Console.WriteLine(vbLf & "TEST KDF FUNCTION:")
			Dim kek As Byte(), zz As Byte(), info As Byte()
			Dim nbytes As Integer
			Dim okhex As String
			' ansx963_2001.rsp CAVS 12.0 'ANS X9.63-2001' information for sample
			nbytes = 128 \ 8
			zz = Cnv.FromHex("96c05619d56c328ab95fe84b18264b08725b85e33fd34f08")
			okhex = "443024c3dae66b95e6f5670601558f71"
			kek = Kdf.Bytes(nbytes, zz, Kdf.KdfAlg.X963, Kdf.HashAlg.Sha256)
			Console.WriteLine("KEK={0}", Cnv.ToHex(kek))
			Console.WriteLine("OK ={0}", okhex)
			Debug.Assert([String].Equals(Cnv.ToHex(kek), okhex, StringComparison.OrdinalIgnoreCase), "Kdf.Bytes failed")

			' [RFC 5869] A.1.  Test Case 1 Basic test case with SHA-256
			nbytes = 42
			zz = Cnv.FromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
			info = Cnv.FromHex("f0f1f2f3f4f5f6f7f8f9")
			okhex = "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"
			kek = Kdf.Bytes(nbytes, zz, Kdf.KdfAlg.Hkdf, Kdf.HashAlg.Sha256, info, "salt=000102030405060708090a0b0c")
			Console.WriteLine("KEK={0}", Cnv.ToHex(kek))
			Console.WriteLine("OK ={0}", okhex)
			Debug.Assert([String].Equals(Cnv.ToHex(kek), okhex, StringComparison.OrdinalIgnoreCase), "Kdf.Bytes failed")

			' [RFC 5869] A.3.  Test with SHA-256 and zero-length salt/info
			nbytes = 42
			zz = Cnv.FromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
			okhex = "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8"
			kek = Kdf.Bytes(nbytes, zz, Kdf.KdfAlg.Hkdf, Kdf.HashAlg.Sha256)
			Console.WriteLine("KEK={0}", Cnv.ToHex(kek))
			Console.WriteLine("OK ={0}", okhex)
			Debug.Assert([String].Equals(Cnv.ToHex(kek), okhex, StringComparison.OrdinalIgnoreCase), "Kdf.Bytes failed")

		End Sub

		Private Shared Sub test_KDF_ForCms()
			Console.WriteLine(vbLf & "TEST KDF FOR CMS:")
			Dim kek As Byte(), zz As Byte(), ukm As Byte()
			Dim okhex As String
			zz = Cnv.FromHex("160E3F5588C6FB4E9CEE8BC3C1C5000AB86396468C3D1CAEC0CB6E21536B5513")
			okhex = "04D616C654CDF62BB186A5A088B60FB5"
			kek = Kdf.ForCms(zz, Kdf.KeyWrapAlg.Aes128_wrap, Kdf.KdfAlg.X963, Kdf.HashAlg.Sha1)
			Console.WriteLine("KEK={0}", Cnv.ToHex(kek))
			Console.WriteLine("OK ={0}", okhex)
			Debug.Assert([String].Equals(Cnv.ToHex(kek), okhex, StringComparison.OrdinalIgnoreCase), "Kdf.Bytes failed")
			' Use same ZZ but use HKDF, SHA-256, and add user key material
			ukm = Cnv.FromHex("616263")
			' "abc"
			okhex = "1D06D6FD5C1EBFB33CAD875E6B99781D3D750875F573C9093CECBFBA6937ACC5"
			kek = Kdf.ForCms(zz, Kdf.KeyWrapAlg.Aes256_wrap, Kdf.KdfAlg.Hkdf, Kdf.HashAlg.Sha256, ukm)
			Console.WriteLine("KEK={0}", Cnv.ToHex(kek))
			Console.WriteLine("OK ={0}", okhex)
			Debug.Assert([String].Equals(Cnv.ToHex(kek), okhex, StringComparison.OrdinalIgnoreCase), "Kdf.Bytes failed")
		End Sub

		Private Shared Sub test_HASH_Length()
			Console.WriteLine(vbLf & "TEST HASH LENGTH:")
			Dim alg As HashAlgorithm
			alg = HashAlgorithm.Sha1
			Console.WriteLine("len({0})={1}", alg.ToString(), Hash.Length(alg))
			alg = HashAlgorithm.Sha256
			Console.WriteLine("len({0})={1}", alg.ToString(), Hash.Length(alg))
			alg = HashAlgorithm.Sha512
			Console.WriteLine("len({0})={1}", alg.ToString(), Hash.Length(alg))
			alg = HashAlgorithm.Ripemd160
			Console.WriteLine("len({0})={1}", alg.ToString(), Hash.Length(alg))
		End Sub

		Private Shared Sub test_Xof()
			Console.WriteLine(vbLf & "TEST XOF FUNCTIONS:")
			Dim nbytes As Integer
			Dim msg As Byte()
			Dim b As Byte()
			Dim okhex As String

			' Ref: "SHA-3 XOF Test Vectors for Byte-Oriented Output"
			' File `SHAKE256VariableOut.rsp` COUNT = 1244
			Console.WriteLine("SHAKE256")
			nbytes = 2000 \ 8
			msg = Cnv.FromHex("6ae23f058f0f2264a18cd609acc26dd4dbc00f5c3ee9e13ecaea2bb5a2f0bb6b")
			okhex = "b9b92544fb25cfe4ec6fe437d8da2bbe" & "00f7bdaface3de97b8775a44d753c3ad" & "ca3f7c6f183cc8647e229070439aa953" & "9ae1f8f13470c9d3527fffdeef6c94f9" & "f0520ff0c1ba8b16e16014e1af43ac6d" & "94cb7929188cce9d7b02f81a2746f52b" & "a16988e5f6d93298d778dfe05ea0ef25" & "6ae3728643ce3e29c794a0370e9ca6a8" & "bf3e7a41e86770676ac106f7ae79e670" & "27ce7b7b38efe27d253a52b5cb54d6eb" & "4367a87736ed48cb45ef27f42683da14" & "0ed3295dfc575d3ea38cfc2a3697cc92" & "864305407369b4abac054e497378dd9f" & "d0c4b352ea3185ce1178b3dc1599df69" & "db29259d4735320c8e7d33e8226620c9" & "a1d22761f1d35bdff79a"
			b = Xof.Bytes(nbytes, msg, Xof.Alg.Shake256)
			Console.WriteLine("OUT={0}", Cnv.ToHex(b))
			Debug.Assert([String].Equals(Cnv.ToHex(b), okhex, StringComparison.OrdinalIgnoreCase), "Xof.Bytes failed")

			' Using MGF1-SHA-256
			' From SPHINCS+ test vectors r.3
			Console.WriteLine("MGF1-SHA-256")
			nbytes = 34
			msg = Cnv.FromHex("3b5c056af3ebba70d4c805380420585562b32410a778f558ff951252407647e3")
			okhex = "5b7eb772aecf04c74af07d9d9c1c1f8d3a90dcda00d5bab1dc28daecdc86eb87611e"
			b = Xof.Bytes(nbytes, msg, Xof.Alg.Mgf1_Sha256)
			Console.WriteLine("OUT={0}", Cnv.ToHex(b))
			Debug.Assert([String].Equals(Cnv.ToHex(b), okhex, StringComparison.OrdinalIgnoreCase), "Xof.Bytes failed")

			' Test other MGF1's
			Console.WriteLine("MGF1-SHA-1")
			nbytes = 24
			msg = Cnv.FromHex("012345ff")
			okhex = "242fb2e7a338ae07e580047f82b7acff83a41ec5d8ff9bab"
			b = Xof.Bytes(nbytes, msg, Xof.Alg.Mgf1_Sha1)
			Console.WriteLine("OUT={0}", Cnv.ToHex(b))
			Debug.Assert([String].Equals(Cnv.ToHex(b), okhex, StringComparison.OrdinalIgnoreCase), "Xof.Bytes failed")

		End Sub

		Private Shared Sub test_Prf()
			Console.WriteLine(vbLf & "TEST PRF FUNCTIONS:")
			Dim nbytes As Integer
			Dim msg As Byte()
			Dim key As Byte()
			Dim b As Byte()
			Dim okhex As String

			' `KMAC_samples.pdf` "Secure Hashing - KMAC-Samples" 2017-02-27
			' Sample #1
			Console.WriteLine("Sample #1: ""standard"" KMAC output length KMAC128 => 256 bits, no custom string")
			nbytes = 256 \ 8
			msg = Cnv.FromHex("00010203")
			key = Cnv.FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F")
			okhex = "E5780B0D3EA6F7D3A429C5706AA43A00FADBD7D49628839E3187243F456EE14E"
			b = Prf.Bytes(nbytes, msg, key, Prf.Alg.Kmac128)
			Console.WriteLine("OUT={0}", Cnv.ToHex(b))
			Debug.Assert([String].Equals(Cnv.ToHex(b), okhex, StringComparison.OrdinalIgnoreCase), "Prf.Bytes failed")

			' Sample #6
			Console.WriteLine("Sample #6: ""standard"" KMAC output length KMAC256 => 512 bits, no custom string")
			nbytes = 512 \ 8
			msg = Cnv.FromHex("000102030405060708090A0B0C0D0E0F" & vbCr & vbLf & "                101112131415161718191A1B1C1D1E1F" & vbCr & vbLf & "                202122232425262728292A2B2C2D2E2F" & vbCr & vbLf & "                303132333435363738393A3B3C3D3E3F" & vbCr & vbLf & "                404142434445464748494A4B4C4D4E4F" & vbCr & vbLf & "                505152535455565758595A5B5C5D5E5F" & vbCr & vbLf & "                606162636465666768696A6B6C6D6E6F" & vbCr & vbLf & "                707172737475767778797A7B7C7D7E7F" & vbCr & vbLf & "                808182838485868788898A8B8C8D8E8F" & vbCr & vbLf & "                909192939495969798999A9B9C9D9E9F" & vbCr & vbLf & "                A0A1A2A3A4A5A6A7A8A9AAABACADAEAF" & vbCr & vbLf & "                B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF" & vbCr & vbLf & "                C0C1C2C3C4C5C6C7")
			key = Cnv.FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F")
			okhex = "75358CF39E41494E949707927CEE0AF20A3FF553904C86B08F21CC414BCFD691589D27CF5E15369CBBFF8B9A4C2EB17800855D0235FF635DA82533EC6B759B69"
			b = Prf.Bytes(nbytes, msg, key, Prf.Alg.Kmac256)
			Console.WriteLine("OUT={0}", Cnv.ToHex(b))
			Debug.Assert([String].Equals(Cnv.ToHex(b), okhex, StringComparison.OrdinalIgnoreCase), "Prf.Bytes failed")

			' Sample #2
			Console.WriteLine("Sample #2: same as Sample #1 except with custom string")
			nbytes = 256 \ 8
			msg = Cnv.FromHex("00010203")
			key = Cnv.FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F")
			okhex = "3B1FBA963CD8B0B59E8C1A6D71888B7143651AF8BA0A7070C0979E2811324AA5"
			b = Prf.Bytes(nbytes, msg, key, Prf.Alg.Kmac128, "My Tagged Application")
			Console.WriteLine("OUT={0}", Cnv.ToHex(b))
			Debug.Assert([String].Equals(Cnv.ToHex(b), okhex, StringComparison.OrdinalIgnoreCase), "Prf.Bytes failed")

		End Sub

		Private Shared Sub test_X509_MakeCert_Internal_X25519()
			Console.WriteLine(vbLf & "CREATE A NEW CERTIFICATE USING INTERNAL KEY STRINGS:")
			Dim certname As String = "new-lamps-dana.encrypt.cer"
			Dim issuercert As String = "lamps-ca.ed25519.crt"
			Dim prikeyfile As String = "lamps-ca.ed25519.p8"
			' No password
			Dim pubkeyfile As String = "lamps-dana.encrypt.crt"
			' We get the public key from the cert we are trying to reproduce...
			' Try and reproduce lamps-dana.encrypt.cer from [RFC9216]
			' (well, not quite, because the order of the extension fields will be different in our cert, so the signature and thumbprint will not match.
			' The content is identical, just in a different order)
			Dim dn As String = "O=IETF;OU=LAMPS WG;CN=Dana Hopper"
			' ECDH with HKDF using SHA-256; uses AES-128 key wrap 
			Dim extns As String = "serialNumber=#x0E4B0A36A9EFBA9C9A3B68248E521DC0DEF3A7;notBefore=2020-12-15T21:35:44;notAfter=2052-12-15T21:35:44;extKeyUsage=emailProtection;" & "keyUsage=keyAgreement;sMIMECapabilities=301A060B2A864886F70D0109100313300B0609608648016503040105;" & "certificatePolicies=2.16.840.1.101.3.2.1.48.1;rfc822Name=dana@smime.example;subjectKeyIdentifier=9ddf4dd405ef9aec6086bc276d04e9ce5adc8fa4"
			' Read in private and public keys to internal key strings
			Dim sbPrikeystr As StringBuilder = Ecc.ReadPrivateKey(prikeyfile, "")
			Debug.Assert(sbPrikeystr.Length > 0, "Ecc.ReadPrivateKey failed")
			Dim sbPubkeystr As StringBuilder = Ecc.ReadPublicKey(pubkeyfile)
			Debug.Assert(sbPubkeystr.Length > 0, "Ecc.ReadPublicKey failed")
			' Create the new certificate
			Dim r As Integer = X509.MakeCert(certname, issuercert, sbPubkeystr.ToString(), sbPrikeystr.ToString(), 0, 0, _
				dn, extns, 0, "", SigAlgorithm.Ed25519, X509.CertOptions.AuthKeyId)
			Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r)
			Debug.Assert(0 = r, "X509.MakeCert failed")
			' Dump details of cert we just made...
			x509DumpFile(certname)
			' For comparison, dump details of original lamps cert
			x509DumpFile(pubkeyfile)
			' NOTE: the *content* of the original certificate is the same as the newly created one 
			' _BUT_ the *order* of extension fields is different, so the thumbprints and signatures will not match.
			' AFAWK there is no accepted order for extensions in an X.509 certificate.

		End Sub

		Private Shared Sub test_CIPHER_File_GCM()
			Console.WriteLine(vbLf & "ENCRYPT FILE USING GCM:")
			Dim filein As String, fileenc As String, filechk As String
			Dim r As Integer

			' 128-bit key (16 bytes)
			Dim key As Byte() = New Byte() {&H2b, &H7e, &H15, &H16, &H28, &Hae, _
				&Hd2, &Ha6, &Hab, &Hf7, &H15, &H88, _
				&H9, &Hcf, &H4f, &H3c}
			' 96-bit IV (12 bytes)
			Dim iv As Byte() = New Byte() {&H0, &H1, &H2, &H3, &H4, &H5, _
				&H6, &H7, &H8, &H9, &Ha, &Hb}

			filein = "excontent.txt"
			Console.WriteLine("Input file '{0}' length {1}", filein, FileLength(filein))
			fileenc = "excontent-gcm-enc.dat"

			' Encrypt the file
			r = Cipher.FileEncrypt(fileenc, filein, key, iv, CipherAlgorithm.Aes128, Mode.GCM, _
				opts := Cipher.Opts.PrefixIV)

			Console.WriteLine("Cipher.FileEncrypt(GCM) returns {0} (expecting 0)", r)
			Debug.Assert(r = 0, "Cipher.FileEncrypt(GCM) failed")
			Console.WriteLine("Encrypted file '{0}' length {1} expected {2}+{3}+{4}", fileenc, FileLength(fileenc), FileLength(filein), 12, 16)

			' Now decrypt it
			filechk = "excontent-gcm-chk.dat"
			r = Cipher.FileDecrypt(filechk, fileenc, key, Nothing, CipherAlgorithm.Aes128, Mode.GCM, _
				opts := Cipher.Opts.PrefixIV)
			Debug.Assert(r = 0, "Cipher.FileDecrypt(GCM) failed")
			Console.WriteLine("Decrypted file '{0}' length {1}", filechk, FileLength(filechk))

			' Check files are equal by comparing their file hashes
			Debug.Assert(Hash.HexFromFile(filein, HashAlgorithm.Sha1) = Hash.HexFromFile(filechk, HashAlgorithm.Sha1), "File hashes do not match")

			Console.WriteLine(vbLf & "Try to write output to file of same name but different form...")
			Dim fileout As String = ".\" & filein
			Console.WriteLine("filein='{0}', fileout='{1}'", filein, fileout)
			r = Cipher.FileEncrypt(fileout, filein, key, iv, CipherAlgorithm.Aes128, Mode.GCM, _
				opts := Cipher.Opts.PrefixIV)
			Console.WriteLine("Cipher.FileEncrypt(GCM) returns {0} (expecting error)", r)
			If r <> 0 Then
				Console.WriteLine(General.FormatErrorMessage(r))
			End If
			Debug.Assert(r <> 0, "Expected error did not happen!")

		End Sub

		Private Shared Sub test_RSA_ReadPublicKey_CSR()
			Console.WriteLine(vbLf & "READ PUBLIC KEY FROM CSR:")
			Dim csrfile As String, keyfile As String, dn As String, extns As String
			Dim keystr As String
			Dim r As Integer

			' Create a new CSR for LAMPS WG alice
			csrfile = "lamps-alice-csr.pem"
			keyfile = "lamps-alice.p8"
			' No password
			dn = "O=IETF;OU=LAMPS WG;CN=Alice Lovelace;"
			extns = "keyUsage=digitalSignature,nonRepudiation;extKeyUsage=emailProtection"

			r = X509.CertRequest(csrfile, keyfile, dn, extns, "", SigAlgorithm.Rsa_Sha256, _
				0)

			Console.WriteLine("X509.CertRequest returns {0} (expecting 0)", r)
			If r <> 0 Then
				Console.WriteLine(General.FormatErrorMessage(r))
			End If
			Debug.Assert(0 = r, "X509.CertRequest failed")

			' Dump details of CSR we just made...
			x509DumpFile(csrfile, X509.OutputOpts.Ldap)

			' Read in public key from this CSR file
			keystr = Rsa.ReadPublicKey(csrfile).ToString()
			Debug.Assert(keystr.Length > 0, "Failed to read public key from CSR")
			Console.WriteLine("Keysize={0} bits, HashCode=0x{1,8:X}", Rsa.KeyBits(keystr), Rsa.KeyHashCode(keystr))
			' EXPECTED: Keysize=2048 bits, HashCode=0xCA0B84DA

		End Sub

		Private Shared Sub test_X509_MakeCert_Ex()
			Console.WriteLine(vbLf & "MAKE X.509 CERT WITH LATEST OPTIONS V20.7:")
			Dim r As Integer
			Dim dn As String, extns As String
			Dim certname As String = "myca-newattributes2022.cer"
			Dim keyfile As String = "lamps-ca.rsa.p8"
			' No password
			Dim serialnum As Integer = &H889
			dn = "C=DE;dnQualifier='distinguisher';initials='E.M.';pseudonym='Super Muster';generationQualifier='3rd.';CN=Erika Mustermann"
			extns = "keyUsage=digitalSignature,nonRepudiation;extKeyUsage=emailProtection;rfc822Name=erika@example.com;cRLDistributionPoints=http://dodgycert.example.com/evca.crl;"
			r = X509.MakeCertSelf(certname, keyfile, serialnum, 10, dn, extns, _
				0, "", SigAlgorithm.Rsa_Sha256, 0)
			Console.WriteLine("X509.MakeCertSelf returns {0} (expecting 0)", r)
			If r <> 0 Then
				Console.WriteLine(General.FormatErrorMessage(r))
			End If
			Debug.Assert(0 = r, "X509.MakeCertSelf failed")
			' Dump details of cert we just made...
			x509DumpFile(certname, X509.OutputOpts.Ldap)


		End Sub
		Private Shared Sub test_CNV_ShortNamePath()
			Console.WriteLine(vbLf & "GET SHORT NAME PATH:")
			Dim longpath As String, shortpath As String

			longpath = "File with a long name and spaces hello there all good yes thanks.txt"
			Console.WriteLine("longpath='{0}'", longpath)
			shortpath = Cnv.ShortPathName(longpath)
			Console.WriteLine("shortpath='{0}'", shortpath)

			longpath = "你好.txt"
			Console.WriteLine("longpath='{0}' (may not print nicely on console)", longpath)
			shortpath = Cnv.ShortPathName(longpath)
			Console.WriteLine("shortpath='{0}'", shortpath)
		End Sub

		Private Shared Sub test_CNV_ShortNamePath_EncryptFile()
			Console.WriteLine(vbLf & "USE SHORT NAME PATH TO ENCRYPT FILE:")
			' 128-bit key (16 bytes)
			Dim key As Byte() = New Byte() {&H2b, &H7e, &H15, &H16, &H28, &Hae, _
				&Hd2, &Ha6, &Hab, &Hf7, &H15, &H88, _
				&H9, &Hcf, &H4f, &H3c}
			' 96-bit IV (12 bytes)
			Dim iv As Byte() = New Byte() {&H0, &H1, &H2, &H3, &H4, &H5, _
				&H6, &H7, &H8, &H9, &Ha, &Hb}
			Dim r As Integer
			Dim longpath As String, shortpath As String
			Dim filein As String, fileenc As String

			' Use a file with Chinese characters in its name (which will fail)...
			longpath = "你好.txt"
			filein = longpath
			fileenc = "cn-enc.dat"
			Console.WriteLine("Try using Chinese filename to call Cipher.FileEncrypt...")
			Console.WriteLine("filein={0}", filein)
			r = Cipher.FileEncrypt(fileenc, filein, key, iv, CipherAlgorithm.Aes128, Mode.GCM, _
				opts := Cipher.Opts.PrefixIV)
			Console.WriteLine("Cipher.FileEncrypt returns {0} (expected error)", r)
			If r <> 0 Then
				Console.WriteLine(General.FormatErrorMessage(r))
			End If
			Console.WriteLine("Get short path name and use that instead...")
			shortpath = Cnv.ShortPathName(longpath)
			filein = shortpath
			Console.WriteLine("filein={0}", filein)
			r = Cipher.FileEncrypt(fileenc, filein, key, iv, CipherAlgorithm.Aes128, Mode.GCM, _
				opts := Cipher.Opts.PrefixIV)
			Console.WriteLine("Cipher.FileEncrypt returns {0} (expected 0)", r)
			If r <> 0 Then
				Console.WriteLine(General.FormatErrorMessage(r))
			End If
			Debug.Assert(r = 0, "Cipher.FileEncrypt failed")
			Console.WriteLine("Created encrypted file '{0}'", fileenc)

			' Now say we want to *output* to a file with Chinese characters in its name
			longpath = "您好加密.dat"
			' 'hello encrypted.txt'
			Console.WriteLine("Now output to a file with Chinese characters in its name '{0}' (name may not display properly)", longpath)
			' So, as a fudge, create a dummy file of that name then get its short path name and overwrite it
			If Not File.Exists(longpath) Then
				File.WriteAllText(longpath, "")
			End If
			shortpath = Cnv.ShortPathName(longpath)
			fileenc = shortpath
			Console.WriteLine("fileenc={0}", fileenc)
			' Overwrite the dummy file
			r = Cipher.FileEncrypt(fileenc, filein, key, iv, CipherAlgorithm.Aes128, Mode.GCM, _
				opts := Cipher.Opts.PrefixIV)
			Console.WriteLine("Cipher.FileEncrypt returns {0} (expected 0)", r)
			If r <> 0 Then
				Console.WriteLine(General.FormatErrorMessage(r))
			End If
			Debug.Assert(r = 0, "Cipher.FileEncrypt failed")
			Console.WriteLine("Created encrypted file '{0}'", fileenc)

			' Display contents of encrypted file
			Dim ct As Byte() = File.ReadAllBytes(fileenc)
			Console.WriteLine("CONTENTS: " & Cnv.ToHex(ct))
			' Decrypt the contents directly in memory
			Dim dt As Byte() = Cipher.Decrypt(ct, key, Nothing, CipherAlgorithm.Aes128, Mode.GCM, opts := Cipher.Opts.PrefixIV)
			Console.WriteLine("DECRYPTED: [" & System.Text.Encoding.[Default].GetString(dt) & "]")

		End Sub



		'*****************
		' FILE UTILITIES *
		'*****************

		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 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 ByteArraysEqual(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

		' ASN.1 DISPLAY CONTENTS
		Private Shared Function asn1DumpFile(infile As String) As Boolean

			If FileLength(infile) >= 0 Then
				Console.WriteLine("FILE: " & infile)
			Else
				Console.WriteLine("STRING: " & infile)
			End If

			Dim s As String = Asn1.TextDumpToString(infile)
			If s.Length > 0 Then
				Console.WriteLine(s)
			Else
				Console.WriteLine("ERROR!")
			End If

			Return (s.Length > 0)
		End Function

		' X.509 DISPLAY
		Private Shared Function x509DumpFile(infile As String, Optional opts As X509.OutputOpts = X509.OutputOpts.[Default]) As Boolean
			If FileLength(infile) >= 0 Then
				Console.WriteLine("FILE: " & infile)
			Else
				Console.WriteLine("STRING: " & infile)
			End If

			Dim s As String = X509.TextDumpToString(infile, opts)
			If s.Length > 0 Then
				Console.WriteLine(s)
			Else
				Console.WriteLine("ERROR:" & General.LastError())
			End If

			Return (s.Length > 0)
		End Function

		'***********************
		' DISPLAY ERROR DETAILS
		'***********************
		''' <summary>
		''' Display details of last error given error code
		''' </summary>
		''' <param name="nErr">Error code returned by last call</param>
		Private Shared Sub disp_error(Optional nErr As Integer = 0)
			Dim s As String = General.FormatErrorMessage(nErr)
			Console.WriteLine(s)
		End Sub

		'**********************
		' HOUSEKEEPING STUFF...
		'**********************

		Private Shared Function SetupTestFilesAndDirectory(arrFileNames As String()) As String
			Dim subdir As String
			'**************************************************
			' 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 = [String].Format("STOPPED: Required file is missing." & vbLf & " Look in {0}", zippedTestFiles)
			For Each fn As String In arrFileNames
				If FileIsNotPresent(fn, missingFile) Then
					Return Nothing
				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())

			Return subdir
		End Function

		'*********************************************************
		' Put CWD back to parent and offer to remove the test dir
		'*********************************************************
		Private Shared Sub RestoreDirectory(subdir As String, askDelete As Boolean)
			Dim s As String

			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


	End Class
End Namespace