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