using System;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using System.Text;

using CryptoSysPKI;

/*  $Id: TestPKIcsharp.cs $ 
 *   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`
 */

namespace TestPKIExamples
{
	/// <summary>
	/// Test examples for CryptoSysPKI interface
	/// </summary>
	class TestPKIExamples
	{
        private const int MIN_PKI_VERSION = 200700;

		// Test files required to exist in the current working directory:
		private static string[] arrFileNames = 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",   /* Unencrypted version */
            "AliceRSAPSS.p8e",  /* Encrypted, password="password" */
            "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
        internal static string zippedTestFiles = "pkiDotNetTestFiles.zip";

		// Global vars set by command-line args
		private static bool doPrompt = false;
		private static bool doBigFile = false;

		
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main(string[] args)
		{
			string origdir;  // 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)
			{
				Console.WriteLine("FATAL ERROR: Require PKI version " + MIN_PKI_VERSION + " or later.");
				return;
			}
            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)
			 */

			bool doSome = false;
			bool askDelete = false;
			// These are global
			doPrompt = false;
			doBigFile = false;
			for (int iarg = 0; iarg < args.Length; iarg++)
			{
				if (args[iarg] == "some")
					doSome = true;
				if (args[iarg] == "prompt")
					doPrompt = true;
				if (args[iarg] == "askdelete")
					askDelete = true;
				if (args[iarg] == "bigfile")
					doBigFile = true;
			}

			// Check required files exist and setup temp directory
			origdir = SetupTestFilesAndDirectory(arrFileNames);
			if (null == origdir) return;


			//*************
			// DO THE TESTS
			//*************
			if (doSome) // 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_CMS_MakeSigData_PSS();
                //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();
           

            }
			else
			{   // Do all the test modules (default)
				DoAllTests();
			}


			// FINALLY, DISPLAY QUICK INFO ABOUT THE CORE DLL
			Console.WriteLine("\nDETAILS 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("\nALL TESTS COMPLETED.");

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

		static void 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();
        }


		//********************
		// THE TEST MODULES...
		//********************

		static void test_General()
		{
			int n;
			char ch;
			string s;
			//****************
			// GENERAL TESTS *
			//****************
			Console.WriteLine("\nGENERAL 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);
		}

		static void test_Tdea()
		{
			//*******************************************
			// TDEA (Triple DES, 3DES) ENCRYPTION TESTS *
            //*******************************************

            string s;
            int n;
			byte[] b;
			string keyhex, plainStr, cipherStr, ivhex, okhex;
			byte[] arrPlain;
			byte[] arrCipher;
			byte[] arrKey;
			byte[] arrIV;
			string excontent, fnameData, fnameEnc, fnameCheck;
			Console.WriteLine("\nTESTING TRIPLE DES:");
			keyhex = "010101010101010101010101010101010101010101010101";
			plainStr = "8000000000000000";
			cipherStr = "95F8A5E5DD31D900";
			// Encrypt in ECB mode using hex strings
			s = Tdea.Encrypt(plainStr, keyhex, Mode.ECB, null);
			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, null);
			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, null);
			Console.WriteLine("CT={0}", Cnv.ToHex(b));
			b = Tdea.Decrypt(arrCipher, arrKey, Mode.ECB, null);
			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, null);
			if (0 == n)
				Console.WriteLine("Tdea.File created encrypted file '{0}'", fnameEnc);
			else
				Console.WriteLine("Tdea.File returned error code {0}", n);
			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, null);
			if (0 == n)
			{
				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);
			Debug.Assert(0 == n, "Tdea.File failed.");

        }

		static void test_Rsa()
		{
			//************
			// RSA TESTS *
			//************
			string s;
			int n, nblock;
			byte[] b;
			bool isok;
			byte[] msg;
			byte[] bcheck;
			StringBuilder sbPrivateKey;
			StringBuilder sbPublicKey;
			string fnameOutput, fname;
			string strCheck;

			Console.WriteLine("\nRSA 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))
				Console.WriteLine("OK, verification OK");
			else
				Console.WriteLine("ERROR: verification failed");
			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, (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");

		}

        static void test_Rsa_XML()
        {
            //*********************
            // RSA TESTS WITH XML *
            //*********************
            string s;
            StringBuilder sbPrivateKey;
            string fldName;
            string xmlkey;

            Console.WriteLine("\nRSA 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:\n{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:\n{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:\n{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):\n{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:\n{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);

        }

		static void test_Pfx()
		{
			//***************************
			// PFX/PKCS-12 FILE FUNCTIONS 
			//***************************
			int n;
			string fname;
			string s;
			string fnameInput, fnameCert, fnameP7;
			string password;

			Console.WriteLine("\nTEST 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");


		}

		static void test_Rsa_Keys()
		{
			//***************************************
			// ADVANCED PRIVATE KEY SAVING OPTIONS 
			// --- OVERLOAD for Rsa.SaveEncPrivateKey
			//***************************************
			int n;
			string fname;
			string s;
			StringBuilder sbPrivateKey;
			StringBuilder sbKeyCheck;

			Console.WriteLine("\nTEST 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);

		}

		static void test_Rsa_X509_Stronger()
		{
			int n;
			string fname;
			int i, k;
			StringBuilder sbPrivateKey;
			StringBuilder sbPublicKey;
			string pubkeyFile, prikeyFile;
			string s;
			string fnameCert;
			string issuerCert;
			string distname;
			string query;
			string certList;
			string cert1, cert2;

			Console.WriteLine("\nSTRONGER 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("\nSTRONGER 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, 0x1, 10, distname, "",
				X509.KeyUsageOptions.KeyCertSign | X509.KeyUsageOptions.DigitalSignature |
				X509.KeyUsageOptions.CrlSign | 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'));

			// 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'));
			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, 0x256, 9, distname, "",
				(X509.KeyUsageOptions)k,
				"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("\nMAKE 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; i <= n; i++)
			{
				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);
			}

			// 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);

		}


		static void test_X509()
		{
			//*************
			// X509 TESTS *
			//*************
			string s;
			int i, n, r;
			bool isok;
			StringBuilder sbPublicKey;
			string fnameCheck;
			string fnameInput, fnameCert, fname;
			string issuerCert;
			string hexDigest;
			string strCheck, certBase64, distname;
			string query;
			string certList, certFile, csrFile;
			string extns, dn, password;
			string pubkeyFile, prikeyFile;

			Console.WriteLine("\nX509 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 | 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)
				Console.WriteLine("OK, {0} was issued by {1}.", fnameCert, issuerCert);
			else
				Console.WriteLine("ERROR: {0} was NOT issued by {1}.", fnameCert, issuerCert);

			fnameCert = "myCAcert.cer";
			issuerCert = "myCAcert.cer";
			n = X509.VerifyCert(fnameCert, issuerCert);
			if (0 == n)
				Console.WriteLine("OK, {0} was issued by {1}.", fnameCert, issuerCert);
			else
				Console.WriteLine("ERROR: {0} was NOT issued by {1}.", fnameCert, issuerCert);

			// 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}\n\t--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)
				Console.WriteLine("OK, {0} is valid now.", fnameCert);
			else
				Console.WriteLine("ERROR: {0} is NOT valid now.", fnameCert);

			// 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=\n{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)
				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");
			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) disp_error(n);
            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; i <= n; i++)
			{
				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);
			}

			// 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, 0x888, 8, distname, "ben@ho.com.cn",
				X509.KeyUsageOptions.DigitalSignature | 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 | 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;";
			X509.KeyUsageOptions kuo = X509.KeyUsageOptions.DigitalSignature | 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) Console.WriteLine(" error=" + General.LastError());
			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, 0x109, 24, "", extns, 0, password, SigAlgorithm.Default, 0);
			Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r);
            if (r != 0) disp_error(r);
			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");


		}

		static void test_CRL()
		{
			//******************************************
			// CRL (CERTIFICATE REVOCATION LIST) TESTS *
			//******************************************
			int r;
			string prikeyFile;
			string issuerCert;
			string certList, certFile, crlFile;
			string extns, password, dateStr;
			
			// [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)
				Console.WriteLine("CERT HAS BEEN REVOKED");
			else if (0 == r)
				Console.WriteLine("Cert has not been revoked");
			else
				Console.WriteLine("ERROR: {0}: {1}", General.ErrorLookup(r), General.LastError());

            // 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)
				Console.WriteLine("CERT HAS BEEN REVOKED");
			else if (0 == r)
				Console.WriteLine("Cert has not been revoked");
			else
				Console.WriteLine("ERROR: {0}: {1}", General.ErrorLookup(r), General.LastError());

			// 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)
				Console.WriteLine("CERT HAS BEEN REVOKED");
			else if (0 == r)
				Console.WriteLine("Cert has not been revoked");
			else
				Console.WriteLine("ERROR: {0}: {1}", General.ErrorLookup(r), General.LastError());


		}

		static void test_OCSP()
		{
			//**************************************************
			// OCSP (ONLINE CERTIFICATE STATUS PROTOCOL) TESTS *
			//**************************************************
			string s;
			string fname;
			string issuerCert;
			string certFile, snStr;

			Console.WriteLine("\nOCSP (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);


		}

		static void test_CMS(bool doBigFile)
		{
			//*****************************
			// CMS (S/MIME OBJECTS) TESTS *
			//*****************************
			string s;
			int n;
			byte[] b;
			byte[] bcheck;
			StringBuilder sbPrivateKey;
			string fnameInput, fnameOutput, fnameCert, fname;
			string hexDigest;
			string strCheck;
			string query;
			string certList;
			long flen;

			Console.WriteLine("\nCMS (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)
			{
				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");
			}

			// 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 | 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 | 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.

		}

        static void test_CMS_SigData_PSS()
        {
            Console.WriteLine("\nCMS SIG-DATA WITH RSA-PSS:");

            // Use German Health examples folder for now
            const string TESTPATH = @"C:\Test\GermanHealth\";
            string prikeyFile = TESTPATH + "999009991b_pri.p8e";
            string userCert = TESTPATH + "999009991b.cer";
            string recipKeyFile = TESTPATH + "999009051b_pri.p8e";
            string recipCert = TESTPATH + "999009051b.cer";
            string certList = recipCert + ";" + TESTPATH + "CA_4096_Cert.cer" + ";" + TESTPATH + "Int_4096_Cert.cer";
            string interFile = TESTPATH + "To_999009051b.int";
            string outFile = TESTPATH + "To_999009051b.p7m";
            string interFile1 = TESTPATH + "To_999009051b_1.int";
            string msg = "Hallo Walt";

            StringBuilder sbPrivateKey;
            int r;
            string s;
            string query;

            // 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 | 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("\nPart 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, '='));
            s = X509.TextDumpToString(recipCert, X509.OutputOpts.Decimal | X509.OutputOpts.Ldap);
            Debug.Assert(s.Length > 0);
            Console.WriteLine(s);
            Console.WriteLine(String.Empty.PadLeft(20, '='));
            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);

        }

		static void test_CMS_EnvData()
		{
			string s;
			int n;
			string fname;
			StringBuilder sbPrivateKey;
			string query;
			string fnameCert;

			Console.WriteLine("\nADVANCED 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");

		}

        static void test_CMS_EnvData_ecdh()
        {
            string s;
            int n;
            string fname;
            StringBuilder sbPrivateKeyAlice, sbPrivateKeyDana;
            string query;
            string certName, certList;
            int nRecips;

            Console.WriteLine("\nENVELOPED-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) Console.WriteLine(General.FormatErrorMessage(n));
            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) Console.WriteLine(General.FormatErrorMessage(n));
            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) Console.WriteLine(General.FormatErrorMessage(n));
            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) Console.WriteLine(General.FormatErrorMessage(n));
            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) Console.WriteLine(General.FormatErrorMessage(n));
            Debug.Assert(nRecips != n, "Cms.MakeEnvDataFromString succeeded when should fail");

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

        }

        static void test_CMS_EnvData_auth()
        {
            string s;
            int n;
            string fname;
            StringBuilder sbPrivateKey;
            string query;
            string fnameCert;

            Console.WriteLine("\nAUTHENTICATED-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) Console.WriteLine(General.FormatErrorMessage(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);

            // 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");


        }

        static void test_CMS_EnvData_pwri()
        {
            string s;
            int n;
            string fname;
            string query;
            StringBuilder sbPassword = new StringBuilder("password1234");

            Console.WriteLine("\nENVELOPED-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) Console.WriteLine(General.FormatErrorMessage(n));
            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);

        }

        static void test_CMS_EnvData_kekri()
        {
            string s;
            int n;
            string fname;
            string query;
            string kekstr = "#x0123456789ABCDEFF0E1D2C3B4A59687";   // Represents KEK of 16 bytes/128 bits suitable for aes128-wrap

            Console.WriteLine("\nENVELOPED-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) Console.WriteLine(General.FormatErrorMessage(n));
            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");

        }

        static void test_CMS_EnvData_examples()
        {
            int n;

            Console.WriteLine("\nENVELOPED-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
            string s;
            string fname;
            StringBuilder sbPrivateKey;

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

        }



		static void test_CMS_SMIME_Chain()
		{
			int r;
			string s;
			string inpFile;
			string outFile;
			string origFile = "sonnets.txt";
			string alicekeyfile = "AlicePrivRSASign.p8e";
			string alicecerfile = "AliceRSASignByCarl.cer";
			string password = "password";
			string bobkeyfile = "BobPrivRSAEncrypt.p8e";
			string bobcerfile = "BobRSASignByCarl.cer";
			StringBuilder sbPrikey;
			string query;
			string digest1, digest2;

			Console.WriteLine("\nCREATE 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("\nReverse 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}')=\t{1}", outFile, digest1);
			digest2 = Hash.HexFromFile(origFile, HashAlgorithm.Sha1);
			Console.WriteLine("SHA1('{0}')=\t{1}", origFile, digest2);
			Debug.Assert(digest1 == digest2, "Digests do not match");


		}

		static void test_Hash()
		{
			//*************
			// HASH TESTS *
			//*************
			string s;
			string fname;
			string strCheck;
			byte[] b;

			Console.WriteLine("\nHASH 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')=\n{0}", s);
			s = Hash.HexFromString("abc", HashAlgorithm.Sha384);
			Console.WriteLine("SHA-384('abc')=\n{0}", s);
			s = Hash.HexFromString("abc", HashAlgorithm.Sha512);
			Console.WriteLine("SHA-512('abc')=\n{0}", s);
			// Create a test file
			fname = "hello.txt";
			File.WriteAllText(fname, "hello world\r\n");
			// 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'))=\n" + Cnv.ToHex(b));
            Debug.Assert(String.Compare(Cnv.ToHex(b), "4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358", true) == 0, "SHA256(SHA256('abc')) failed");
		}

        static void test_Hash_SHA3()
        {
            //*************
            // SHA-3 HASH TESTS *
            //*************
            string s;
            string fname;
            byte[] b;

            Console.WriteLine("\nSHA-3 HASH DIGEST TESTS:");
            s = Hash.HexFromString("abc", HashAlgorithm.Sha3_224);
            Console.WriteLine("SHA-3-224('abc'):\n{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'):\n{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'):\n{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'):\n{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')=\n" + 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'))=\n" + 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'))=\n" + Cnv.ToHex(b));
        }

		static void test_Hmac()
		{
			//*************
			// HMAC TESTS *
			//*************
			string s;
			int i;
			byte[] b;
			byte[] key;
			byte[] msg;
			string strCheck;

			Console.WriteLine("\nHMAC 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')=\n{0}", s);
			strCheck = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843";
			Console.WriteLine("CORRECT=\n{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[25];
			for (i = 0; i < 25; i++)
				key[i] = (byte)(i + 1);
			Console.WriteLine("Key={0}", Cnv.ToHex(key));
			msg = new byte[50];
			for (i = 0; i < 50; i++)
				msg[i] = 0xcd;
			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)=\n{0}", Cnv.ToHex(b));
			strCheck = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b";
			Console.WriteLine("CORRECT=\n{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)=\n{0}", s);
			strCheck = "896FB1128ABBDF196832107CD49DF33F47B4B1169912BA4F53684B22";
			Console.WriteLine("Correct                            =\n{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)=\n{0}", s);
			strCheck = "B0344C61D8DB38535CA8AFCEAF0BF12B881DC200C9833DA726E9376C2E32CFF7";
			Console.WriteLine("Correct                            =\n{0}", strCheck);
			Debug.Assert(String.Compare(s, strCheck, true) == 0, "HMAC-SHA-256('Hi There', (0x0b)*20) failed");

		}

        static void test_Hmac_SHA3()
        {
            //*************
            // HMAC-SHA3 TESTS *
            //*************
            string s;
            byte[] b;
            byte[] key;
            byte[] msg;
            string strCheck;

            Console.WriteLine("\nHMAC-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
                202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F
                404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F
                606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F
                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: \n{0}", s);
            Console.WriteLine("Correct:        \n{0}", strCheck);
            Debug.Assert(String.Compare(s, strCheck, true) == 0, "HMAC-SHA3-512('Sample #3) failed");
        }

		static void test_Wipe()
		{
			//******************
			// WIPE DATA TESTS *
			//******************
			string s;
			byte[] b;
			bool isok;
			string fname;
			StringBuilder sbPrivateKey;

			Console.WriteLine("\nWIPE 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}", (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());


		}

		static void test_Prompt(bool doPrompt)
		{
			//************************
			// PASSWORD PROMPT TESTS *
			//************************
			string s;
			if (doPrompt)
			{
				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);
			}
		}

		static void test_Rng(bool doPrompt)
		{
			string s;
			int i, n;
			byte[] b;
			string fname;
			bool isok;

			//************
			// RNG TESTS *
			//************
			Console.WriteLine("\nGENERATE SOME RANDOM DATA...");
			for (i = 0; i < 3; i++)
			{
				b = Rng.Bytes(24);
				Console.WriteLine("RNG={0}", Cnv.ToHex(b));
			}
			// And some random numbers in a given range
			Console.Write("{random x : -10<=x<=+10} = ");
			for (i = 0; i < 12; i++)
			{
				n = Rng.Number(-10, +10);
				Console.Write("{0} ", n);
			}
			Console.Write("\n");

			// Initialize with a seed file
			fname = "seed.dat";
			if (!FileExists(fname))
			{	// No seed file yet, so we'll make one
				if (doPrompt)
				{   // 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];
                    File.WriteAllBytes(fname, b);
				}
			}
			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; i < 3; i++)
			{
				b = Rng.Bytes(24);
				Console.WriteLine("RNG={0}", Cnv.ToHex(b));
			}
			// 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)
			{
				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));
			}

			// 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[4] { 0xde, 0xad, 0xbe, 0xef };
			Console.WriteLine("'seed'={0}", Cnv.ToHex(b));
			b = Rng.Bytes(16, b);
			Console.WriteLine("RNG={0}", Cnv.ToHex(b));

			// Do some tests on the RNG function
			fname = "Fips140.txt";
			isok = Rng.Test(fname);
			Console.WriteLine("Rng.Test('{0}') returns {1} (expecting True)", fname, isok);
			Debug.Assert(true==isok, "Rng.Test failed");
			isok = Rng.Test("");
			Console.WriteLine("Rng.Test('{0}') returns {1} (expecting True)", "", isok);
			Debug.Assert(true==isok, "Rng.Test failed");


		}
        //****************************************
        // GENERIC BLOCK CIPHER ENCRYPTION TESTS *
        //****************************************

		static void test_Cipher()
		{
			byte[] b;
			int n;
			byte[] key, plain, cipher, iv;

			Console.WriteLine("\nTESTING CIPHER(tdea, bytes):");
			// Encrypt in CBC mode using byte arrays
			key = Cnv.FromHex("737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32");
			iv = Cnv.FromHex("B36B6BFB6231084E");
			plain = Cnv.FromHex("5468697320736F6D652073616D706520636F6E74656E742E0808080808080808");
			cipher = 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));
			Debug.Assert(ByteArraysEqual(b, cipher), "Cipher.Encrypt{bytes,tdea-CBC} failed");
			// Decrypt
			b = Cipher.Decrypt(cipher, 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 = 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));
            Debug.Assert(ByteArraysEqual(b, cipher), "Cipher.Encrypt{bytes,Aes128-CBC} failed");
            // Decrypt
            b = Cipher.Decrypt(cipher, 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...");
            byte [] badkey = new byte[23];
            b = Cipher.Encrypt(plain, key, null, 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...");
            byte[] badiv = new byte[7];
            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...");
            byte[] badblk = new byte[31];
            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.");
		}

        static void test_Cipher_Hex()
        {
            string s;
            int n;
            string keyhex, plainStr, cipherStr, ivhex;

            Console.WriteLine("\nTESTING 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.");


        }
		static void test_Cipher_Size()
        {
            CipherAlgorithm alg;

            Console.WriteLine("\nBLOCK 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));
        }

        static void test_Cipher_File()
        {
            Console.WriteLine("\nTESTING CIPHER FILE:");
            // Encrypt random data of random length using alg/mode selected at random

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

            CipherAlgorithm alg;
            Mode mode;
            Padding pad;
            Cipher.Opts opt;
            byte[] key;
            byte[] iv;
            byte[] b;
            char[] buf;
            string s, s1;
            int len, r, counter;
            int ntests = 5;

            Console.WriteLine("NTESTS=" + ntests);
            for (counter = 0; counter < ntests; counter++) {
                // 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];
                for (int i = 0; i < len; i++)
                {
                    // random printable character in [' ', '~']
                    char ch = (char)Rng.Number(0x20, 0x7e);
                    buf[i] = ch;
                }
                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 = modes[Rng.Number(0, modes.Length - 1)];
                Console.WriteLine("MODE=" + mode.ToString());
                // Choose type of padding, if relevant
                if (mode == Mode.ECB || mode == Mode.CBC) {
                    pad = paddings[Rng.Number(0, paddings.Length - 1)];
                } else {
                    // Note: padding is ignored for other modes
                    pad = Padding.NoPad;
                }
                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 != Mode.ECB) {
                    iv = Rng.Bytes(Cipher.BlockBytes(alg));
                    Console.WriteLine("IV:" + Cnv.ToHex(iv));
                } else {
                    iv = null;
                }

                // Encrypt
                r = Cipher.FileEncrypt(file_ct, file_pt, key, iv, alg, mode, 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 & Cipher.Opts.PrefixIV) == Cipher.Opts.PrefixIV) {
                    // IV is not required if already exists prefixed to file
                    iv = null;
                }
                r = Cipher.FileDecrypt(file_ck, file_ct, key, iv, alg, mode, 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);

            }

        }

		static void test_KeyWrap()
		{
			// **************
			// KEY WRAPPING
			// **************
			byte[] arrPlain, arrKey;
			byte[] arrCipher, bcheck;
			Console.WriteLine("\nKEY 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.");

		}


		static void test_Autack()
		{
			string s;
			byte[] b;
			byte[] msg;
			string hexDigest;
			string strCheck;
			string xmlKey, msgStr, keyStr;
			int keyBits;
			// *********************
			// AUTACK SIGNATURES 
			// *********************
			// (This is specialist stuff)
			Console.WriteLine("\nAUTACK 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=\n{0}", Cnv.ToHex(b));
			// Sign block with RSA private key
			// -- use special RSA2 method with magic nibble value 6
			b = Rsa.RawPrivate(b, keyStr, 0x6);
			// Convert to hex encoding
			s = Cnv.ToHex(b);
			Console.WriteLine("Autack Signature=\n{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, 0x6);
			Console.WriteLine("Decrypted block=\n{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");

 
		}

		static void test_PEM()
		{
			string s;
			int r;
            string fnameInput, fnameOutput, fnameOutputUnix;
			string hexDigest;
			string strCheck;

		   // *********************
			// PEM FILE CONVERSIONS
			// *********************
			Console.WriteLine("\nPEM 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");


		}

		static void test_Padding()
		{
			string s;
			int i;
			byte[] b;

			// *************************
			// PADDING FOR BLOCK CIPHERS
			// *************************
			Console.WriteLine("\nPADDING FOR BLOCK CIPHERS:");
			// 1. Pad a byte array of 5 bytes for Triple DES ECB/CBC mode
			b = new byte[5] { 0xff, 0xff, 0xff, 0xff, 0xff };
			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[18];
			for (i = 0; i < b.Length; i++) {
				b[i] = 0xFF;
			}
			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[0];
			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);

		}

        static void test_Cnv_hex()
        {
            string s;
            byte[] b;
            //**************************
            // HEX CONVERSION TESTS *
            //**************************
            Console.WriteLine("\nHEX 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());
        }

        static void test_Cnv_base64()
        {
            string s;
            byte[] b;
            string b64str;
            //**************************
            // BASE64 CONVERSION TESTS *
            //**************************
            Console.WriteLine("\nBASE64 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");


        }

		static void test_Cnv_other()
		{
			string s;
			int n;
			byte[] b;
			string fname;
            string s1;
			byte[] b1;

			//*******************
			// CONVERSION TESTS *
			//*******************
			Console.WriteLine("\nOTHER 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[] { 0x61, 0x93, 0x86, 0x87, 0x8c, 0x94, 0x62 };
			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
			Encoding encod = 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[] { 0xef, 0xbf, 0xbf };
			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[] { 0xc3, 0xb3, 0xc3, 0xa9, 0xc3, 0xad, 0xc3, 0xa1, 0xc3 };
			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));


		}

		static void test_Cnv_base58()
		{
			string s;
			byte[] b;
			string b58str;
			//**************************
			// BASE58 CONVERSION TESTS *
			//**************************
            // Fixed typos [2021-10-17]
			Console.WriteLine("\nBASE58 CONVERSION TESTS:");
			b58str = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM";
			b = Cnv.FromBase58(b58str);
			Console.WriteLine("'" + b58str + "'->\n0x" + 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");
		}

		static void test_CipherPad()
		{
			string s;
			byte[] key, iv, pt, ct, p1, correct;

			Console.WriteLine("\nTEST 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));


		}

		static void test_Pbkdf2()
		{
			//***************
			// PBKDF2 TESTS *
			//***************
			int n;
			byte[] arrKey;
			string strCheck;
			string keyStr;
			byte[] arrPwd, salt;
			string saltHex;

			Console.WriteLine("\nTESTING PBKDF2...");
			// convert password string to bytes
			arrPwd = System.Text.Encoding.Default.GetBytes("password");
			// make a salt
			salt = new byte[] { 0x78, 0x57, 0x8e, 0x5a, 0x5d, 0x63, 0xcb, 0x06 };
			// 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");


		}

		static void test_Asn1()
		{
			//***************
			// ASN.1 TESTS *
			//***************
			string s;
			string fname;
			string b64str;
			b64str = // Alice's RSA public key as a base64 string
				"MIGJAoGBAOCJczmN2PX16Id2OX9OsAW7U4PeD7er3H3HdSkNBS5tEt+mhibU" +
				"0m+qWCn8l+z6glEPMIC+sVCeRkTxLLvYMs/GaG8H2bBgrL7uNAlqE/X3BQWT" +
				"3166NVbZYf8Zf8mB5vhs6odAcO+sbSx0ny36VTq5mXcCpkhSjE7zVzhXdFdf" +
				"AgMBAAE=";

			Console.WriteLine("\nTESTING 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);

		}

		static void test_Sig()
		{
			int n;
			byte[] b;
			string s, correct_sha1, correct_sha256;
			string keyFile, password, certFile, dataFile;

			//***************
			// SIG TESTS *
			//***************
			Console.WriteLine("\nTESTING 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");
			correct_sha1 = // Alice/'abc'/SHA-1
				"YK1aePtKQDDsVCyJdM0V9VOE6DZVTO3ZoyLV9BNcYmep0glwxU5mUQcLAUTUOETImTIN2Pp4Gffr" +
				"xqdxUoczLshnXBNhg7P4ofge+WlBgmcTCnVv27LHHZpmdEbjTg6tnPMb+2b4FvMZ0LfkMKXyiRVTmG4A" +
				"NyAmHH6QIsDZ8R8=";
			s = Sig.SignData(b, keyFile, password, SigAlgorithm.Default);
			Console.WriteLine("Sig.signData returns \n'" + 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");
			correct_sha256 = // Alice/'abc'/SHA-256
				"tLy6hJadL4w9JI/A/qLCG0Vz1fWPIrPMWD8NGmA5wP7HHlUID54elztUYrpdm9RFeh0RCMJ618dw" +
				"BpgIutuZg2qQ2i9uMUYB0DvZvkyeD6MqmtVa4ihgc9SLqhigKeP+KB7voEi7PH3hpEr9Wa3kn4mbPpeD" +
				"1VHSzgu/qirjOaA=";
			s = Sig.SignData(b, keyFile, password, SigAlgorithm.Rsa_Sha256);
			Console.WriteLine("Sig.signData returns \n'" + 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 \n'" + 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 \n'" + 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 \n'" + 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");

		}

		private static Boolean checkSig(String sig64, SigAlgorithm sigAlg, String publicKeyStr)
		{
			// 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
			byte[] sig = Cnv.FromBase64(sig64);
			// 2. Decrypt using internal public key string
			byte[] b = Rsa.RawPublic(sig, 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"
			byte[] digest = Rsa.DecodeDigestForSignature(b);
			Console.WriteLine("digest=" + Cnv.ToHex(digest));
			// 4. Now encode for signature using specified digest algorithm
			// -- this should match exactly
			HashAlgorithm hashAlg = Sig.GetHashAlgFromSigAlg(sigAlg);
			byte[] b1 = Rsa.EncodeDigestForSignature(b.Length, digest, hashAlg);
			Console.WriteLine("Re-encoded block with " + hashAlg + ":");
			Console.WriteLine(Cnv.ToHex(b1));
			Debug.Assert(ByteArraysEqual(b, b1), "Blocks are not equal");

			return true;
		}

		static void test_Sig_Vars()
		{
			int r;
			byte[] b;
			String s;
			String keyFile, password, certFile;
			String publicKeyStr;
			SigAlgorithm sigAlg;

			Console.WriteLine("\nTESTING 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(" + sigAlg + ") returns \n" + 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(" + sigAlg + ") returns \n" + 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(" + sigAlg + ") returns \n" + 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(" + sigAlg + ") returns \n" + 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(" + sigAlg + ") returns \n" + 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(" + sigAlg + ") returns \n" + s);
			checkSig(s, sigAlg, publicKeyStr);
			r = Sig.VerifyData(s, b, publicKeyStr, sigAlg);
			Console.WriteLine("Sig.VerifyData returns " + r + " (expected 0)");
			Debug.Assert(0 == r);

		}

		static void test_Sig_Ecc()
		{
			int n;
			string s;
			byte[] b;
			byte[] digest;
			string sig;
			string fname;
			string prikeyfile = "prime256v1.p8";
			string pubkeyfile = "prime256v1.pub";
			SigAlgorithm sigAlg = SigAlgorithm.Ecdsa_Sha256;
			StringBuilder sbPassword = new StringBuilder("password");
			StringBuilder sb;
			HashAlgorithm hashAlg;

			Console.WriteLine("\nTESTING 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 = Sig.SignData(b, prikeyfile, sbPassword.ToString(), sigAlg);
			// NB signature value will be different each time
			Console.WriteLine("SIG: " + sig);

			// 4. Verify that the signature over the DATA
			n = Sig.VerifyData(sig, 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 = Sig.SignDigest(digest, prikeyfile, sbPassword.ToString(), sigAlg);
			Console.WriteLine("SIG: " + sig);

			// And verify the signature using the digest value
			n = Sig.VerifyDigest(sig, 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 = Sig.SignFile(fname, prikeyfile, sbPassword.ToString(), sigAlg);
			Console.WriteLine("SIG: " + sig);

			// And verify the signature over the file
			n = Sig.VerifyFile(sig, fname, pubkeyfile, sigAlg);
			Console.WriteLine("Sig.VerifyFile returns " + n + " (expected 0)");
			Debug.Assert(0 == n);

			// Finally, wipe the password
			Wipe.String(sbPassword);
		}

		static void test_Sig_Ecc_det()
		{
			string sig, intKey, intPubKey;
			byte[] b;
			int n;
			string hexKey;
			Ecc.CurveName curveName;
			string sigdetok = "0f2141a0ebbc44d2e1af90a50ebcfce5e197b3b7d4de036deb18bc9e1f3d7387500cb99cf5f7c157070a8961e38700b7";

			Console.WriteLine("\nTESTING 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 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1, 
				Sig.SigOptions.UseDeterministic, Sig.Encoding.Base16);
			Console.WriteLine("SIG(hex): " + sig);
			Console.WriteLine("SIG(OK) : " + sigdetok);
			Debug.Assert(String.Compare(sig, 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)), 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 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1,
				Sig.SigOptions.UseDeterministic | Sig.SigOptions.Asn1DERStructure, Sig.Encoding.Base16);
			Console.WriteLine("SIG(asn1der): " + sig);
			// VERIFY IT AGAIN
			n = Sig.VerifyData(sig, 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 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1,
				Sig.SigOptions.UseDeterministic | Sig.SigOptions.Asn1DERStructure, 0);
			Console.WriteLine("SIG(asn1der): " + sig);
			// VERIFY IT AGAIN
			n = Sig.VerifyData(sig, 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 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1,
				Sig.SigOptions.UseDeterministic | Sig.SigOptions.Asn1DERStructure, Sig.Encoding.Base64url);
			Console.WriteLine("SIG(base64url): " + sig);
			// VERIFY IT AGAIN (note the encoding is detected automatically by Sig.Verifydata)
			n = Sig.VerifyData(sig, 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 = Sig.SignData(b, intKey, "", SigAlgorithm.Ecdsa_Sha1);
			Console.WriteLine("SIG(random-k): " + sig);
			// VERIFY IT AGAIN
			n = Sig.VerifyData(sig, b, intPubKey, SigAlgorithm.Ecdsa_Sha1);
			Console.WriteLine("Sig.VerifyData returns " + n + " (expected 0)");
			Debug.Assert(0 == n);

		}

		static void test_Sig_Ecc_btc()
		{
			int r;
			string s;
			string sig, internalKey, internalPubKey;
			string hexKey;
			Ecc.CurveName curveName;
			string datahex;
			byte[] data, digest;
			string sigweb = "3045022100da43201760bda697222002f56266bf65023fef2094519e13077f777baed553b102205ce35d05eabda58cd50a67977a65706347cc25ef43153e309ff210a134722e9e";
			string pubkeyok = "042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9";
			string sigdetok = "30450220587ce0cf0252e2db3a7c3c91b355aa8f3385e128227cd8727c5f7777877ad772022100edc508b7c14891ed15ab38c687019d7ebaf5c12908cf21a83e8ae57e8c47e95c";

			Console.WriteLine("\nTESTING 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')=\n" + s);
			Console.WriteLine("OK PUBKEY:\n" + 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:\n" + 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)):\n" + Cnv.ToHex(digest));
			Console.WriteLine("Same hash value but reversed:\n" + 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 = Sig.SignDigest(digest, internalKey, "", SigAlgorithm.Ecdsa_Sha256,
				Sig.SigOptions.UseDeterministic | Sig.SigOptions.Asn1DERStructure, Sig.Encoding.Base16);
			Console.WriteLine("SIG (DET): " + sig);
			Debug.Assert(sig == 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, 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);

		}

		static void test_Ecc_MakeKeys()
		{
			int n;
			string s;
			string pubkeyfile;
			string prikeyfile;
			StringBuilder sbIntKey;
            int h1, h2;

			Console.WriteLine("\nTESTING 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')=\n[" + 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);

		}

        static void test_ECC_Brainpool()
        {
            int n;
            string s;
            string pubkeyfile;
            string prikeyfile;
            StringBuilder sbIntKey;
            int h1, h2;
            string sigval;
            int r;

            Console.WriteLine("\nTESTING 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')=\n[" + 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("\nAgain 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')=\n[" + 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...");
            byte[] msg = 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) Console.WriteLine(General.FormatErrorMessage());
            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);

        }

		static void test_Ecc_MakeReadSaveKeys()
		{
			int n;
			string s;
			string pubkeyfile;
			string prikeyfile, fname;
			StringBuilder sbIntKey;
			string query;

			Console.WriteLine("\nTESTING 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);
		}


		static void test_Ecc_KeyByCurve()
		{
			string s;
			string hexKey;
			Ecc.CurveName curveName;
			string internalKey;
			string query;
			string b58Key;

			Console.WriteLine("\nTESTING 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: " + 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: " + 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);

		}

		static void test_Ecc_SaveKey()
		{
			int n;
			string s;
			string hexKey;
			Ecc.CurveName curveName;
			string intPriKey, intPubKey;
			string query;
			string fname;

			Console.WriteLine("\nTESTING 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: " + 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);

		}

		// BYTE UTILITIES
		static void test_Byte_Utils()
		{
			byte[] b;
			// NOTE: this is only place we explicitly use an unsigned integer
			uint n;

			Console.WriteLine("\nTESTING BYTE UTILS...");

			Console.WriteLine("Testing Cnv.ReverseBytes()...");
			b = new byte[5] { 0x01, 0x02, 0x03, 0x04, 0x05 };
			Console.WriteLine("(before)=" + Cnv.ToHex(b));
			b = Cnv.ReverseBytes(b);
			Console.WriteLine("(after)= " + Cnv.ToHex(b));
			Debug.Assert(b[0] == 0x05 && b[4] == 0x01, "Cnv.ReverseBytes failed");
			b = new byte[4] { 0x01, 0x02, 0x03, 0x04 };
			Console.WriteLine("(before)=" + Cnv.ToHex(b));
			b = Cnv.ReverseBytes(b);
			Console.WriteLine("(after)= " + Cnv.ToHex(b));
			Debug.Assert(b[0] == 0x04 && b[3] == 0x01, "Cnv.ReverseBytes failed");
			b = new byte[1] { 0x01 };
			Console.WriteLine("(before)=" + Cnv.ToHex(b));
			b = Cnv.ReverseBytes(b);
			Console.WriteLine("(after)= " + Cnv.ToHex(b));
			Debug.Assert(b[0] == 0x01, "Cnv.ReverseBytes failed");

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

			Console.WriteLine("Testing Cnv.NumFromBytes()...");
			b = new byte[4] { 0xde, 0xad, 0xbe, 0xef };
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.BigEndian);
			Console.WriteLine("(" + Cnv.ToHex(b) + ",BE)->0x" + n.ToString("x8"));
			Debug.Assert(0xdeadbeef == n);
			n = Cnv.NumFromBytes(b, Cnv.EndianNess.LittleEndian);
			Console.WriteLine("(" + Cnv.ToHex(b) + ",LE)->0x" + n.ToString("x8"));
			Debug.Assert(0xefbeadde == 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"));

		}

        static void test_Compress()
        {
            Console.WriteLine("\nCOMPRESS DATA USING ZLIB COMPRESSION:");
            byte[] data, cdata, udata;
            // 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));

        }

        //************************************
        // CODE FORMERLY IN TestPKISharpV12.cs
        //************************************

        static void test_MakeRSA_ECCKeys()
        {
            /* Demonstrates:
             * Rsa.MakeKeys()   (Updated [v20.3])
             * Ecc.MakeKeys()
             */
            int r;
            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);
        }

        static void test_ReadRSA_ECCKeys()
        {
            Console.WriteLine("\nShow 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()
             */

            StringBuilder sbPriKey;
            string pubkey;
            int r;

            // 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));
        }

        static void test_MakeCerts_ECDSA()
        {
            Console.WriteLine("\nCreate 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()
             */

            int r;
            X509.KeyUsageOptions kuFlags;
            string dn, extns;
            string s, query, pubkey;

            // Create a self-signed CA certificate
            string ca_cert = "CA_ECC_P256.cer";
            kuFlags = X509.KeyUsageOptions.KeyCertSign | X509.KeyUsageOptions.CrlSign | 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 | X509.OutputOpts.Ldap);
            Console.WriteLine(s);

            // Use the CA certificate to create an end-user certificate
            string user_cert = "User_ECC_P256.cer";
            kuFlags = X509.KeyUsageOptions.DataEncipherment | X509.KeyUsageOptions.DigitalSignature | 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 | 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) disp_error(r);
            Debug.Assert(0 == r);

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

            string user_csr = "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) disp_error(r);
            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
            string user2_cert = "User2_ECC_P256.cer";
            extns = "notBefore=2018-01-02;";
            r = X509.MakeCert(user2_cert, ca_cert, user_csr, "CA_ECC_P256.p8e", 0xECD5A2, 4, "", extns, 0, "password", SigAlgorithm.Ecdsa_Sha256, 0);
            Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r);
            if (r != 0) disp_error(r);
            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("\nThe CA creates a Certificate Revocation List (CRL) revoking User2's certificate as of 1 April 2018");
            string crlFile = "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) disp_error(r);
            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, null, "");
            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
            string datestr = "2018-02-01";
            r = X509.CheckCertInCRL(user2_cert, crlFile, null, datestr);
            Console.WriteLine("X509.CheckCertInCRL('{1}',{2}) returns {0} (expecting 0=NOT REVOKED)", r, crlFile, datestr);
            Debug.Assert(0 == r);


        }

        static void test_MakeCerts_PSS()
        {
            Console.WriteLine("\nCreate 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()
             */

            int r;
            X509.KeyUsageOptions kuFlags;
            string dn, extns;
            string s, query, pubkey;

            // Create a self-signed CA certificate
            string ca_cert = "CA_RSA_2048.cer";
            kuFlags = X509.KeyUsageOptions.KeyCertSign | X509.KeyUsageOptions.CrlSign | 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 | X509.OutputOpts.Ldap);
            Console.WriteLine(s);

            // Use the CA certificate to create an end-user certificate
            string user_cert = "User_RSA_2048.cer";
            kuFlags = X509.KeyUsageOptions.DataEncipherment | X509.KeyUsageOptions.DigitalSignature | 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
            string certpath = ca_cert + ";" + user_cert;
            r = X509.ValidatePath(certpath);
            Console.WriteLine("X509.ValidatePath('{0}') returns {1} (expecting 0)", certpath, r);
            if (r != 0) disp_error(r);
            Debug.Assert(0 == r);

        }

        static void test_SIG_VerifyData_PSS()
        {
            Console.WriteLine("\nVERIFY A SIGNATURE VALUE:");

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

            int r;
            string rsakeyvalue, msghex, S;
            string rsaprikey;
            byte[] msg, digest;

            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) disp_error(r);
            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) disp_error(r);
            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[] { 0x61, 0x62, 0x63 };  // "abc"
            // Extract RSA public key from private
            string pubkeystr = 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) disp_error(r);
            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) disp_error(r);
            Debug.Assert(0 == r);

        }

        static void test_SIG_SignData_PSS()
        {
            Console.WriteLine("\nSIGN 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()
             */

            byte[] msg;
            string sigval, oksig;
            int r;

            string certFile = "AliceRSAPssSignByCarl.cer";
            string priKeyFile = "AliceRSAPSS.p8e"; // pkcs8-encrypted 
            string mypassword = "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) disp_error();
            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) disp_error(r);
            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 | 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) disp_error(r);
            Debug.Assert(0 == r);

            Console.WriteLine("\nSign a file using RSA-PSS-SHA512 with salt length set to the maximum possible; output encoded in hex");
            string dataFile = "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) disp_error();
            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) disp_error(r);
            Debug.Assert(0 == r);

        }

        static void test_SIG_SignData_ECDSA()
        {
            Console.WriteLine("\nSIGN DATA USING ECDSA:");

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

            byte[] msg;
            string sigval, oksig;
            int r;

            string certFile = "User_ECC_P256.cer";
            string priKeyFile = "User_ECC_P256.p8e"; // pkcs8-encrypted 
            string mypassword = "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) disp_error(r);
            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 | 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) disp_error(r);
            Debug.Assert(0 == r);
        }

        static void test_RSA_EncryptDecrypt()
        {
            Console.WriteLine("\nENCRYPT/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()
             */

            string encHex, msgHex, correctHex;
            byte[] encmsg, msg, outmsg;

            // RSA key file 1024-bit from pkcs-1v2-1-vec
            string priKeyFile = "rsa-oaep-1.p8";    // PKCS#8 unencrypted PrivateKeyInfo
            string pubKeyFile = "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));

        }
        static void test_RSA_ToXMLStringEx()
        {
            Console.WriteLine("\nCreate an XML string representation of an RSA internal key string with 'ds:' namespace prefix...");

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

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

        static void test_CMS_MakeSigData_PSS()
        {
            Console.WriteLine("\nCreate a signed-data object using RSA-PSS...");

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

            string outFile = "SignedData_PSS.p7s";
            string inFile = "excontent.txt";
            string certFile = "User_RSA_2048.cer";
            string priKeyFile = "User_RSA_2048.p8e";
            int r;
            string s, query;
            StringBuilder sbPriKey = 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:\n{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);

        }

        static void test_MakeSignedEnveloped_PSS_OAEP()
        {
            Console.WriteLine("\nCreate an enveloped-data object using RSA-OAEP\n"
                + " 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()
             */

            int r;
            string s, query;
            string msg = "Hi Bob, this is a secret message from Alice.";
            string tempFile = "intermediate.tmp";
            string envDataFile = "ToBobSignedByAlice.p7m";
            StringBuilder sbPriKey;
            // 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 | 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("\nBob decrypts the file and verifies the signed-data...");
            string sigDataFile = "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);

        }

        static void test_PFX_MakeFile_PSS()
        {
            Console.WriteLine("\nCreate a PFX file using RSA-PSS...");

            string pfxFile = "AliceRSAPSS.pfx";
            string keyFile = "AliceRSAPSS.p8e";
            string certFile = "AliceRSAPssSignByCarl.cer";
            int r;
            string keyStr;

            r = Pfx.MakeFile(pfxFile, certFile, keyFile, "password", null, 0);
            Console.WriteLine("Pfx.MakeFile() returns {0} (expecting 0)", r);
            if (r != 0) disp_error(r);
            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));
        }

        static void test_PFX_MakeFile_AES256()
        {
            Console.WriteLine("\nCreate a PFX file using AES256-SHA256...");

            string pfxFile = "bob-aes256.pfx";
            string plainKeyFile = "lamps-bob.p8.pem";
            string keyFile = "lamps-bob.p8e";
            string certFile = "lamps-bob.crt";
            string password = "password"; // BAD!!
            int r;
            string keyStr, certStr;
            bool isok;


            // 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) disp_error(r);
            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"));

        }

        static void test_CIPHER_EncryptAEAD()
        {
            Console.WriteLine("\nENCRYPT USING AEAD...");

            byte[] key, iv, pt, aad;
            byte[] ct, dt;
            string okhex;

            // 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) disp_error();
            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, null, AeadAlgorithm.Aes_128_Gcm, Cipher.Opts.PrefixIV);
            Console.WriteLine("CT={0}", Cnv.ToHex(ct));
            Console.WriteLine("Decrypt...");
            dt = Cipher.DecryptAEAD(ct, key, iv, null, 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) disp_error();
            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, null, AeadAlgorithm.Aes_192_Gcm, Cipher.Opts.PrefixIV);
            Console.WriteLine("CT={0}", Cnv.ToHex(ct));
            Console.WriteLine("OK={0}", okhex);
            if (ct.Length == 0) disp_error();
            Debug.Assert(ByteArraysEqual(ct, Cnv.FromHex(okhex)));
            Console.WriteLine("Decrypt...");
            dt = Cipher.DecryptAEAD(ct, key, iv, null, AeadAlgorithm.Aes_256_Gcm, Cipher.Opts.PrefixIV);
            Console.WriteLine("DT={0}", Cnv.ToHex(dt));
            Debug.Assert(ByteArraysEqual(dt, pt));
        }

        static void test_X509_ReadCertFromP7Chain()
        {
            Console.WriteLine("\nREAD CERTS AS BASE64 STRING FROM P7 CHAIN DATA...");

            int ncerts, i;
            string certstr;
            string s;
            // Input is a P7 chain file as a string in PEM format
            // bob.p7b (contains 2 X.509 certs: BobRSA and CarlRSA)
            string p7str =
                "-----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; i <= ncerts; i++) {
                certstr = X509.ReadCertStringFromP7Chain(p7str, i);
                Console.WriteLine("{0}", certstr);
                // Query the cert for subjectName
                s = X509.QueryCert(certstr, "subjectName");
                Console.WriteLine("subjectName='{0}'", s);
            }
        }

        static void test_X509_ReadCertFromPFX()
        {
            Console.WriteLine("\nREAD CERT AS BASE64 STRING FROM PFX...");

            string certstr;
            string s;
            // Input is a PFX file as a string in PEM format
            // bob.pfx (password="password")
            string pfxStr =
                "-----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);
        }


        // NEW IN [v12.2]

        static void test_Cms_Data_Bytes()
        {
            Console.WriteLine("\nCMS SIGNED DATA USING RAW BYTES:");
            // We will create a signed-data object with "raw" content as a UTF-8-encoded XML document.

            string sigdataFile = "sigDataByAlice.p7m";
            string prikeyFile = "AlicePrivRSASign.p8e";
            string certFile = "AliceRSASignByCarl.cer";
            StringBuilder sbPrivateKey;
            int r;
            string query, s;
            byte[] contentArr;

            // Create an XML document as a string with lots of obscure characters
            // and a Byte-Order Mark (BOM).
            string xmlStr = "\ufeff<?xml version=\"1.0\"?><doc>\n" +
                "<name c='es'>Íñigo</name>\n" +
                "<name c='fr'>Françoise</name>\n" +
                "<name c='pl'>Błażej</name>\n" +
                "<name c='cn'>大卫</name>\n" +
                "</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
            byte[] xmlArr = 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
            string contentStr = 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
            string xmlFile = "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("\nCMS 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
            string p7file = "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));

            string envdataFile = "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");
            byte[] arrMsg = Cms.ReadEnvDataToBytes(envdataFile, "", sbPrivateKey.ToString());
            // Display in hex
            Console.WriteLine("Received message in hex:");
            Console.WriteLine(Cnv.ToHex(arrMsg));		
        }

        static void test_ReadJWK()
        {
            Console.WriteLine("\nREAD IN RSA KEY REPRESENTED AS JSON JWK:");
            // RSA public key as a JSON string
            // Ref: RFC 7517 JSON Web Key (JWK) Appendix A.1
            string json = "{\"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);
            StringBuilder sbPublicKey = 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()));
        }

        // NEW IN [v12.3]

        static void test_Cipher_Encrypt_Prefix()
        {
            Console.WriteLine("\nENCRYPT WITH PREFIXED IV xmlenc#aes128-cbc:");
            string plain = "<encryptme>hello world</encryptme>";
            string ciphervalue;
            byte[] key = Cnv.FromHex("6162636465666768696A6B6C6D6E6F70");
            byte[] iv = Rng.Bytes(Cipher.BlockBytes(CipherAlgorithm.Aes128));
            byte[] pt = 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));
            byte[] ct = 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));
            byte[] dt = Cipher.Decrypt(ct, key, null, 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));
        }

        static void test_X509_MakeCert_EmptyDN()
        {
            Console.WriteLine("\nMAKE CERT WITH EMPTY DN:");
            string certname = "AliceRSA-emptyDN.cer";
            string issuerCert = "CarlRSASelf.cer";
            string prikeyfile = "CarlPrivRSASign.p8e";
            string password = "password";
            string subjectPubKeyFile = "AlicePubRSA.pub";
            string dn = "$";    // special flag for empty DN
            string extns = "iPAddress=192.168.15.1";    // at least one field for subject alt name is required
            X509.KeyUsageOptions keyUsage = X509.KeyUsageOptions.DigitalSignature | 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)
            int r = X509.MakeCert(certname, issuerCert, subjectPubKeyFile, prikeyfile, 0x1001, 2, dn, extns, keyUsage, password, 
                SigAlgorithm.Rsa_Sha256, X509.CertOptions.AuthKeyId);
            if (r != 0) disp_error(r);
            Debug.Assert(0 == r, "X509.MakeCert failed");
            Console.WriteLine("Created new X509 file '{0}'", certname);
            x509DumpFile(certname);

        }
        static void test_X509_CertRequest_EmptyDN_extKeyUsage()
        {
            Console.WriteLine("\nMAKE CERTIFICATE SIGNING REQUEST WITH EMPTY DN AND EXTENDED KEY USAGE:");
            string csrfile = "req_emptydn_extkeyusage.p10";
            string epkfile = "AlicePrivRSASign.p8e";
            string password = "password";
            string dn = "$";    // special flag for empty DN
            // Use extensions parameter to add alt subject name and extended key usage flags
            string extns = "iPAddress=192.168.15.1;extKeyUsage=serverAuth,clientAuth,emailProtection,critical;"; 
            int r;

            string certfile = "certfromcsr_emptydn_extkeyusage.cer";
            string issuerCert = "CarlRSASelf.cer";
            string issuerEpkfile = "CarlPrivRSASign.p8e";
            string issuerPassword = "password";
            string query;
            string s;

            // 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) disp_error(r);
            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, 0x10b, 4, "", "", 0, issuerPassword, SigAlgorithm.Rsa_Sha256, X509.CertOptions.Default);
            if (r != 0) disp_error(r);
            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);
        }

        static void test_X509_ReadCertStringFromPFX_3des()
        {
            Console.WriteLine("\nREAD 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
            string pfxfile = "bob-lamps.p12";
            string password = "bob";
            string okcerthash = "6688f64f99744f7faf1059e2b49d1f5042bb2406";
            Console.WriteLine("FILE: {0}", pfxfile);
            // Extract cert as a base64 string
            string certstr = 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
            string digest = 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
            string asn1type = Asn1.Type(certstr);
            Console.WriteLine("Asn1.Type(CER)={0}", asn1type);
        }

        static void test_PFX_MakeFile_3DES()
        {
            Console.WriteLine("\nCREATE A NEW PFX FILE USING 3DES TO ENCRYPT THE CERT:");

            string epkfile = "BobPrivRSAEncrypt.p8e";
            string certfile = "BobRSASignByCarl.cer";
            string pfxfile = "bob-3des.pfx";
            string password = "password";

            // Use StrongCert option to encrypt cert using "stronger" 3DES instead of weak default 40-bit RC2.
            int r = 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
            string asn1type = 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);

        }

        static void test_Rng_Guid()
        {
            Console.WriteLine("\nGENERATE RANDOM GUID (UUID) STRINGS:");
            string s;
            for (int i = 0; i < 5; i++) {
                s = Rng.Guid();
                Console.WriteLine(s);
            }

        }

        // [v12.3.1]

        static void test_CMS_MakeSigData_signingcert()
        {
            Console.WriteLine("\nMAKE CMS SIGNED DATA WITH SIGNING CERTIFICATE ATTRIBUTE:");
            string xmldata = "<a>Some sample content.</a>";
            string certfile = "alice-lamps.crt";
            string keyfile = "alice-lamps.p12";
            string password = "alice";
            string outfile = "signedbyalice-signingcert.p7m";

            // Read in private key to an ephemeral string
            StringBuilder sbPrivateKey = Rsa.ReadPrivateKey(keyfile, password);

            // Set options
            Cms.SigDataOptions sdopts = Cms.SigDataOptions.AltAlgId | Cms.SigDataOptions.IncludeAttributes 
                | Cms.SigDataOptions.AddSignTime | Cms.SigDataOptions.AddSigningCertificate;

            // Create signed-data object using RSA-SHA256
            int r = Cms.MakeSigDataFromString(outfile, xmldata, certfile, sbPrivateKey.ToString(), Cms.SigAlg.Rsa_Sha256, sdopts);
            Console.WriteLine("Cms.MakeSigDataFromString returns {0} (expected 0)", r);
            asn1DumpFile(outfile);
        }

        // [v20.0.0]

        static void test_CIPHER_EncryptHex()
        {
            Console.WriteLine("\nENCRYPT DATA USING HEX-ENCODED PARAMETERS:");
            // Aes128/CBC/OneAndZeroes
            string keyHex = "0123456789ABCDEFF0E1D2C3B4A59687";
            string ivHex = "FEDCBA9876543210FEDCBA9876543210";
            // "Now is the time for all good men to"
            string plainHex = "4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F";
            string okHex = "C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E1771D4CDA34FBFB7E74B321F9A2CF4EA61B";
            string cipherHex, chkHex;
            CipherAlgorithm alg = CipherAlgorithm.Aes128;
            Mode mode = Mode.CBC;
            Padding pad = Padding.OneAndZeroes;
            Console.WriteLine("ALG: {0}/{1}/{2}", alg, mode, 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, pad);
            Console.WriteLine("CT={0}", cipherHex);
            Debug.Assert(cipherHex == okHex, "Cipher.Encrypt failed");
            chkHex = Cipher.Decrypt(cipherHex, keyHex, ivHex, alg, mode, 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, 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, pad, Cipher.Opts.PrefixIV);
            Console.WriteLine("P'={0}", chkHex);
            Debug.Assert(chkHex == plainHex, "Cipher.Decrypt failed");
            Console.WriteLine("P'='{0}'", Cnv.StringFromHex(chkHex));
        }

        static void test_RSA_XML_withprefixes()
        {
            Console.WriteLine("\nCONVERT BETWEEN RSA KEY VALUE AND XML KEY FORM:");
            // Read in private key from key file
            string keyfile = "AlicePrivRSASign.p8e";
            Console.WriteLine("FILE: {0}", keyfile);
            string prikeystr = 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
            string xmlstr = 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])
            string pubkeystr = 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");

        }

        static void test_ECC_DHSharedSecret()
        {
            Console.WriteLine("\nECDH 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]
            Ecc.CurveName curve = Ecc.CurveName.P_256;
            // Our private key is dUIT:
            string ourPrivateKeyHex = "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534";
            // Their public key as hex is "04" || QCAVSx || QCAVSy
            string theirPublicKeyHex = "04" + "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
	            + "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
            // Correct expected result
            string zzOK = "46FC62106420FF012E54A434FBDD2D25CCC5852060561E68040DD7778997BD7B";
            string ourPrivateKey = Ecc.ReadKeyByCurve(ourPrivateKeyHex, curve);
            Console.WriteLine("Curve = {0}", Ecc.QueryKey(ourPrivateKey, "curveName"));
            Console.WriteLine("Our private key has {0} bits", Ecc.QueryKey(ourPrivateKey, "keyBits"));
            string theirPublicKey = Ecc.ReadKeyByCurve(theirPublicKeyHex, curve);
            Console.WriteLine("Their public key has {0} bits", Ecc.QueryKey(theirPublicKey, "keyBits"));

            // Compute shared secret
            byte[] zz = Ecc.DHSharedSecret(ourPrivateKey, theirPublicKey);
            Console.WriteLine("ZZ={0}", Cnv.ToHex(zz));
            Debug.Assert(Cnv.ToHex(zz) == zzOK, "Ecc.DHSharedSecret failed");
        }

        static void test_ECC_DHSharedSecret_25519()
        {
            Console.WriteLine("\nECDH 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
            */
            Ecc.CurveName curve = Ecc.CurveName.X25519;

            string alicePrivateKeyHex = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
            string alicePublicKeyHex = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
            string bobPrivateKeyHex = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb";
            string bobPublicKeyHex = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f";
            // Correct expected result
            string zzOK = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742";

            // 1. Alice's private + Bob's public
            // NB we *must* specify whether X25519 keys are public or private
            string ourPrivateKey = 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"));
            string theirPublicKey = Ecc.ReadKeyByCurve(bobPublicKeyHex, curve, Ecc.KeyType.PublicKey);
            Console.WriteLine("Their public key has {0} bits", Ecc.QueryKey(theirPublicKey, "keyBits"));
            // Compute shared secret
            byte[] zz1 = 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
            byte[] zz2 = Ecc.DHSharedSecret(ourPrivateKey, theirPublicKey);
            Console.WriteLine("ZZ={0}", Cnv.ToHex(zz2));
            Debug.Assert(Cnv.ToHex(zz2).ToLower() == zzOK, "Ecc.DHSharedSecret failed");
        }

        static void test_SIG_SignData_Ed25519()
        {
            Console.WriteLine("\nSIGN 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)
            string privateKey = 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"
            byte[] message = Cnv.FromHex("ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f");
            string sig = Sig.SignData(message, privateKey, "", SigAlgorithm.Ed25519, 0, Sig.Encoding.Base16);
            Console.WriteLine("SIGNATURE:\n{0}", sig);
            // Check against known correct result
            Debug.Assert(sig == "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704");

            // Now verify using the public key
            string publicKey = 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"));

            int r = Sig.VerifyData(sig, message, publicKey, SigAlgorithm.Ed25519);
            Console.WriteLine("Sig.VerifyData returns {0} (expected 0)", r);
            Debug.Assert(0 == r);
        }

        static void test_X509_MakeCertSelf_Ed25519()
        {
            Console.WriteLine("\nCREATE A SELF-SIGNED X.509 CERTIFICATE USING Ed25519:");
            // Ref: [RFC8410] https://tools.ietf.org/html/rfc8410

            string dn, extns, certname, prikeyfile, query;
            string pubkeystr, issuercert;
            X509.KeyUsageOptions keyUsage;
            int r;
            string s;

            // 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 | X509.KeyUsageOptions.KeyCertSign | X509.KeyUsageOptions.CrlSign;
            r = X509.MakeCertSelf(certname, prikeyfile, 0x0ED25519, 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");

        }

        static void test_CMS_MakeSigData_Ed25519()
        {
            Console.WriteLine("\nCREATE A CMS SIGNED-DATA OBJECT USING Ed25519:");

            string outFile = "SignedData_Ed25519.p7m";
            string inFile = "excontent.txt";
            string certFile = "Ed25519-ietf-selfsigned.cer";    // Self-signed cert created using private Ed25519 key in [RFC8410]
            string priKeyFile = "edwards-ietf-ex.p8";   // No password, from [RFC8410]
            int r;
            string s, query;
            // Read in private key to internal key string
            StringBuilder sbPriKey = 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
            Cms.SigDataOptions opts = Cms.SigDataOptions.IncludeAttributes | 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:\n{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");

        }


        static void test_RSA_SaveEncKey()
        {
            Console.WriteLine("\nREAD THEN SAVE AN ENCRYPTED RSA KEY:");
            int r;
            string keyFile = "AlicePrivRSASign.p8e";
            Console.WriteLine("FILE: {0}", keyFile);
            // Read in private key to internal key string
            StringBuilder sbPriKey = 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
            string s = 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);

            StringBuilder sbPriKey1 = 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");
        }

        static void test_General_FormatErrorMessage()
        {
            Console.WriteLine("\nFORMAT ERROR MESSAGE:");
            Console.WriteLine(General.FormatErrorMessage(11));
            Console.WriteLine(General.FormatErrorMessage(11), "User message!");
            // Try and read missing file
            string s = Asn1.Type("missing.file");
            Console.WriteLine(General.FormatErrorMessage());
            int r = 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
            StringBuilder sbPrivKey = 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!"));

        }
        static void test_KDF_Bytes()
        {
            Console.WriteLine("\nTEST KDF FUNCTION:");
            byte[] kek, zz, info;
            int nbytes;
            string okhex;
            // 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");
        
        }

        static void test_KDF_ForCms()
        {
            Console.WriteLine("\nTEST KDF FOR CMS:");
            byte[] kek, zz, ukm;
            string okhex;
            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");
        }

        static void test_HASH_Length()
        {
            Console.WriteLine("\nTEST HASH LENGTH:");
            HashAlgorithm alg;
            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));
        }

        static void test_Xof()
        {
            Console.WriteLine("\nTEST XOF FUNCTIONS:");
            int nbytes;
            byte[] msg;
            byte[] b;
            string okhex;

            // 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");

        }

        static void test_Prf()
        {
            Console.WriteLine("\nTEST PRF FUNCTIONS:");
            int nbytes;
            byte[] msg;
            byte[] key;
            byte[] b;
            string okhex;

            // `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
                101112131415161718191A1B1C1D1E1F
                202122232425262728292A2B2C2D2E2F
                303132333435363738393A3B3C3D3E3F
                404142434445464748494A4B4C4D4E4F
                505152535455565758595A5B5C5D5E5F
                606162636465666768696A6B6C6D6E6F
                707172737475767778797A7B7C7D7E7F
                808182838485868788898A8B8C8D8E8F
                909192939495969798999A9B9C9D9E9F
                A0A1A2A3A4A5A6A7A8A9AAABACADAEAF
                B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF
                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");

        }

        static void test_X509_MakeCert_Internal_X25519()
        {
	        Console.WriteLine("\nCREATE A NEW CERTIFICATE USING INTERNAL KEY STRINGS:");
	        string certname = "new-lamps-dana.encrypt.cer";
	        string issuercert = "lamps-ca.ed25519.crt";
	        string prikeyfile = "lamps-ca.ed25519.p8";      // No password
	        string pubkeyfile = "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)
	        string dn = "O=IETF;OU=LAMPS WG;CN=Dana Hopper";
	        string extns = "serialNumber=#x0E4B0A36A9EFBA9C9A3B68248E521DC0DEF3A7;notBefore=2020-12-15T21:35:44;notAfter=2052-12-15T21:35:44;extKeyUsage=emailProtection;"
                + "keyUsage=keyAgreement;sMIMECapabilities=301A060B2A864886F70D0109100313300B0609608648016503040105;" /* ECDH with HKDF using SHA-256; uses AES-128 key wrap */
		        + "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
            StringBuilder sbPrikeystr = Ecc.ReadPrivateKey(prikeyfile, "");
            Debug.Assert(sbPrikeystr.Length > 0, "Ecc.ReadPrivateKey failed");
            StringBuilder sbPubkeystr = Ecc.ReadPublicKey(pubkeyfile);
            Debug.Assert(sbPubkeystr.Length > 0, "Ecc.ReadPublicKey failed");
            // Create the new certificate
            int r = 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.

        }

        static void test_CIPHER_File_GCM()
        {
            Console.WriteLine("\nENCRYPT FILE USING GCM:");
            string filein, fileenc, filechk;
            int r;

            byte[] key = new byte[] {   // 128-bit key (16 bytes)
		        0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
		        0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C,
            };
            byte[] iv = new byte[] {    // 96-bit IV (12 bytes)
		        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		        0x08, 0x09, 0x0A, 0x0B,
            };

            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, null, 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("\nTry to write output to file of same name but different form...");
            string fileout = ".\\"  + 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) Console.WriteLine(General.FormatErrorMessage(r));
            Debug.Assert(r != 0, "Expected error did not happen!");

        }

        static void test_RSA_ReadPublicKey_CSR()
        {
            Console.WriteLine("\nREAD PUBLIC KEY FROM CSR:");
            string csrfile, keyfile, dn, extns;
            string keystr;
            int r;

            // 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) Console.WriteLine(General.FormatErrorMessage(r));
            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

        }

        static void test_X509_MakeCert_Ex()
        {
            Console.WriteLine("\nMAKE X.509 CERT WITH LATEST OPTIONS V20.7:");
            int r;
            string dn, extns;
            string certname = "myca-newattributes2022.cer";
            string keyfile = "lamps-ca.rsa.p8"; // No password
            int serialnum = 0x889;
            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) Console.WriteLine(General.FormatErrorMessage(r));
            Debug.Assert(0 == r, "X509.MakeCertSelf failed");
            // Dump details of cert we just made...
            x509DumpFile(certname, X509.OutputOpts.Ldap);


        }
        static void test_CNV_ShortNamePath()
        {
            Console.WriteLine("\nGET SHORT NAME PATH:");
            string longpath, shortpath;

            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);
        }

        static void test_CNV_ShortNamePath_EncryptFile()
        {
            Console.WriteLine("\nUSE SHORT NAME PATH TO ENCRYPT FILE:");
            byte[] key = new byte[] {   // 128-bit key (16 bytes)
		        0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
		        0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C,
            };
            byte[] iv = new byte[] {    // 96-bit IV (12 bytes)
		        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		        0x08, 0x09, 0x0A, 0x0B,
            };
            int r;
            string longpath, shortpath;
            string filein, fileenc;

            // 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) Console.WriteLine(General.FormatErrorMessage(r));
            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) Console.WriteLine(General.FormatErrorMessage(r));
            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 (!File.Exists(longpath)) {
                File.WriteAllText(longpath, "");
            }
            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) Console.WriteLine(General.FormatErrorMessage(r));
            Debug.Assert(r == 0, "Cipher.FileEncrypt failed");
            Console.WriteLine("Created encrypted file '{0}'", fileenc);

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

        }


        
        //*****************
		// FILE UTILITIES *
		//*****************

		static long MakeALargeTextFile(string fileName)
		{   // Make a large (~10MB) text file. Return length.
			int targetsize = 1024 * 1024 * 10;
			int nblocks;
			string s;
			StringBuilder sb = new StringBuilder();

			// Make a string of all printable ASCII chars
			for (int i = 32; i <= 126; i++)
			{
				sb.Append((char)i);
			}
			sb.Append('\r');
			sb.Append('\n');
			s = sb.ToString();

			// Write out the text file
			nblocks = targetsize / s.Length;
			FileStream fs;
			StreamWriter sw;
			fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
			sw = new StreamWriter(fs);
			for (int i = 0; i < nblocks; i++)
			{
				sw.Write(s);
			}
			sw.Close();
			fs.Close();

			return FileLength(fileName);
		}

		static bool FileExists(string filePath)
		{
			FileInfo fi = new FileInfo(filePath);
			return fi.Exists;
		}

		static long FileLength(string filePath)
		{
			FileInfo fi = new FileInfo(filePath);
			return fi.Length;
		}
		
		static bool FileIsNotPresent(string filePath, string message)
		{
			if (!FileExists(filePath)) {
				Console.WriteLine("\n{0}: {1}", message, filePath);
				return true;
			}
			return false;
		}

		// Amazingly, .NET doesn't have a Compare Bytes function...
		static bool ByteArraysEqual (byte[] data1, byte[] data2)
		{	// Thanks to Jon Skeet http://www.pobox.com/~skeet
			// If both are null, they're equal
			if (data1==null && data2==null)
			{
				return true;
			}
			// If either but not both are null, they're not equal
			if (data1==null || data2==null)
			{
				return false;
			}
			if (data1.Length != data2.Length)
			{
				return false;
			}
			for (int i=0; i < data1.Length; i++)
			{
				if (data1[i] != data2[i])
				{
					return false;
				}
			}
			return true;
		}

		// ASN.1 DISPLAY CONTENTS
		static bool asn1DumpFile(string infile)
		{

			if (FileLength(infile) >= 0)
				Console.WriteLine("FILE: " + infile);
			else
				Console.WriteLine("STRING: " + infile);

			string s = Asn1.TextDumpToString(infile);
            if (s.Length > 0)
				Console.WriteLine(s);
			else
				Console.WriteLine("ERROR!");

            return (s.Length > 0);
		}

        // X.509 DISPLAY
        static bool x509DumpFile(string infile, X509.OutputOpts opts = X509.OutputOpts.Default)
        {
            if (FileLength(infile) >= 0)
                Console.WriteLine("FILE: " + infile);
            else
                Console.WriteLine("STRING: " + infile);

            string s = X509.TextDumpToString(infile, opts);
            if (s.Length > 0)
                Console.WriteLine(s);
            else
                Console.WriteLine("ERROR:" + General.LastError());

            return (s.Length > 0);
        }

        //***********************
        // DISPLAY ERROR DETAILS
        //***********************
        /// <summary>
        /// Display details of last error given error code
        /// </summary>
        /// <param name="nErr">Error code returned by last call</param>
        static void disp_error(int nErr = 0)
        {
            string s = General.FormatErrorMessage(nErr);
            Console.WriteLine(s);
        }
        
        //**********************
		// HOUSEKEEPING STUFF...
		//**********************

		static string SetupTestFilesAndDirectory(string[] arrFileNames)
		{
			string subdir;
			//**************************************************
			// Check we have required files in local directory *
			//**************************************************
			string assemblyFile = Assembly.GetExecutingAssembly().Location;
			string assemblyDir = Path.GetDirectoryName(assemblyFile);
			Console.WriteLine("Local directory is '{0}'.", assemblyDir);
			Console.WriteLine("Checking required test files are in local directory...");
			string missingFile = String.Format("STOPPED: Required file is missing.\n Look in {0}", zippedTestFiles);
			foreach (string fn in arrFileNames)
			{
				if (FileIsNotPresent(fn, missingFile)) return null;
			}

			//*************************************************
			// 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
			foreach (string fn in arrFileNames)
			{
				System.IO.File.Copy(fn, subdir + "\\" + fn, true);
			}
			// Change current working directory to sub-dir
			System.IO.Directory.SetCurrentDirectory(subdir);
			Console.WriteLine("CWD is " + System.IO.Directory.GetCurrentDirectory());

			return subdir;
		}

		//*********************************************************
		// Put CWD back to parent and offer to remove the test dir
		//*********************************************************
		static void RestoreDirectory(string subdir, bool askDelete)
		{
			string s;

			System.IO.Directory.SetCurrentDirectory("..");
			Console.WriteLine("\nCWD reset to " + System.IO.Directory.GetCurrentDirectory());
			if (askDelete)
			{
				Console.Write("The temp test directory '{0}' was created by this program.\nDo you want to remove it? ([Y]/N) ", subdir);
				s = Console.ReadLine();
				if ("N" != s && "n" != s)
				{
					// Remove directory
					Console.WriteLine("Removing test directory...");
					System.IO.Directory.Delete(subdir, true);
				}
				else
				{
					Console.WriteLine("Temp directory '{0}' left in place.", subdir);
				}
			}
			else
			{
				// Remove directory regardless
				Console.WriteLine("Removing test directory '{0}'", subdir);
				System.IO.Directory.Delete(subdir, true);
			}
		}


	}
}