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

Imports CryptoSysPKI

'  $Id: TestPKIcsharpV12.vb $ 
' *   Last updated:
' *   $Date: 2020-03-6 09:13 $
' *   $Version: 12.3.0 $
' 


' Some tests demonstrating new features in CryptoSys PKI v12.1 
' * in particular RSA-PSS and ECC signatures in X.509 certificates.
' * 
' * Test files are in `pkiDotNetTestFiles.zip`. These must be in the CWD.
' 


'****************************** LICENSE ***********************************
' * Copyright (C) 2018-20 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>
'****************************************************************************
'


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

Namespace TestPKIcsharpV12
	Class TestPKIcsharpV12
		Private Const MIN_PKI_VERSION As Integer = 120100
		' Test files required to exist in the current working directory:
		' Unencrypted version 
		' Encrypted, password="password" 
		Private Shared arrFileNames As String() = New String() {"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"}
		' Name of file with zipped test files
		Private Shared zippedTestFiles As String = "pkiDotNetTestFiles.zip"

		Public Shared Sub Main(args As String())
			Console.WriteLine("TESTING CRYPTOSYS PKI FOR CHANGES IN V12.0 AND V12.1")

			' 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

			' Deal with command-line options
			Dim doSome As Boolean = False
			Dim askDelete As Boolean = False
			For iarg As Integer = 0 To args.Length - 1
				If args(iarg) = "some" Then
					doSome = True
				End If
				If args(iarg) = "askdelete" Then
					askDelete = True
				End If
			Next

			' Check required files exist and setup temp directory
			Dim origdir As String = 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
				' //test_MakeKeys();  // Only do this once! 
				'test_ReadKeys();
				'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();
				' NEW IN [v12.1] 

					'test_X509_ReadCertFromP7Chain();
					'test_X509_ReadCertFromPFX();
				test_CIPHER_EncryptAEAD()
			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}]", General.ModuleName())


			'********************************************
			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()
			' //test_MakeKeys();  // Only do this once! 

			test_ReadKeys()
			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()
			' New in [v12.1]
			test_CIPHER_EncryptAEAD()
			test_X509_ReadCertFromP7Chain()
			test_X509_ReadCertFromPFX()
		End Sub


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


		Private Shared Sub test_MakeKeys()
			' Demonstrates:
'             * Rsa.MakeKeys()
'             * 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", 2048, Rsa.PublicExponent.Exp_EQ_65537, 80, "password", _
				Rsa.PbeOptions.Pbe_Pbkdf2_aes256_CBC, False)
			r = Rsa.MakeKeys("User_RSA_2048.pub", "User_RSA_2048.p8e", 2048, Rsa.PublicExponent.Exp_EQ_65537, 80, "password", _
				Rsa.PbeOptions.Pbe_Pbkdf2_aes256_CBC, False)

			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_ReadKeys()
			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_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


		' END TESTS
		'******************************************************

		'*****************
		' FILE UTILITIES *
		'*****************
		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

		'******************
		' SETUP 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 = "STOPPED: Required file is missing." & vbLf & " Look in '" & 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

		'***********************
		' DISPLAY ERROR DETAILS
		'***********************
		''' <summary>
		''' Display details of last error given error code
		''' </summary>
		''' <param name="nErr">Error code returned</param>
		Private Shared Sub disp_error(nErr As Integer)
			Dim s As String = General.LastError()
			Console.WriteLine("ERROR Returned={0}/{1}: {2}; {3}", nErr, General.ErrorCode(), General.ErrorLookup(nErr), s)
		End Sub

		''' <summary>
		''' Display details of last error (no error code)
		''' </summary>
		Private Shared Sub disp_error()
			Dim s As String = General.LastError()
			Dim nErr As Integer = General.ErrorCode()
			Console.WriteLine("ERROR {0}: {1}: {2}", nErr, General.ErrorLookup(nErr), s)
		End Sub

		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

	End Class

End Namespace