using System; using System.Diagnostics; using System.Reflection; using System.IO; using System.Text; using CryptoSysPKI; /* $Id: TestPKIcsharpV12.cs $ * Last updated: * $Date: 2020-03-6 09:13 $ * $Version: 12.3.0 $ */ /* Some tests demonstrating new features in CryptoSys PKI v12.1 * in particular RSA-PSS and ECC signatures in X.509 certificates. * * Test files are in `pkiDotNetTestFiles.zip`. These must be in the CWD. */ /******************************* LICENSE *********************************** * Copyright (C) 2018-20 David Ireland, DI Management Services Pty Limited. * All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net> * The code in this module is licensed under the terms of the MIT license. * For a copy, see <http://opensource.org/licenses/MIT> **************************************************************************** */ namespace TestPKIcsharpV12 { class TestPKIcsharpV12 { private const int MIN_PKI_VERSION = 120100; // Test files required to exist in the current working directory: private static string[] arrFileNames = new string[] { "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", }; // Name of file with zipped test files private static string zippedTestFiles = "pkiDotNetTestFiles.zip"; static void Main(string[] args) { Console.WriteLine("TESTING CRYPTOSYS PKI FOR CHANGES IN V12.0 AND V12.1"); // Make sure minimum required version of CryptoSys PKI is installed... Console.WriteLine("PKI Version is " + General.Version()); if (General.Version() < MIN_PKI_VERSION) { Console.WriteLine("FATAL ERROR: Require PKI version " + MIN_PKI_VERSION + " or later."); return; } // Deal with command-line options bool doSome = false; bool askDelete = false; for (int iarg = 0; iarg < args.Length; iarg++) { if (args[iarg] == "some") doSome = true; if (args[iarg] == "askdelete") askDelete = true; } // Check required files exist and setup temp directory string 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 /* //test_MakeKeys(); // Only do this once! */ //test_ReadKeys(); //test_MakeCerts_ECDSA(); //test_MakeCerts_PSS(); //test_SIG_VerifyData_PSS(); //test_RSA_EncryptDecrypt(); //test_SIG_SignData_PSS(); //test_SIG_SignData_ECDSA(); //test_RSA_ToXMLStringEx(); //test_CMS_MakeSigData_PSS(); //test_MakeSignedEnveloped_PSS_OAEP(); //test_PFX_MakeFile_PSS(); /* NEW IN [v12.1] */ test_CIPHER_EncryptAEAD(); //test_X509_ReadCertFromP7Chain(); //test_X509_ReadCertFromPFX(); } 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}]", General.ModuleName()); //******************************************** Console.WriteLine("\nALL TESTS COMPLETED."); // Put CWD back to original and offer to remove the test dir RestoreDirectory(origdir, askDelete); } static void DoAllTests() { /* //test_MakeKeys(); // Only do this once! */ test_ReadKeys(); test_MakeCerts_ECDSA(); test_MakeCerts_PSS(); test_SIG_VerifyData_PSS(); test_RSA_EncryptDecrypt(); test_SIG_SignData_PSS(); test_SIG_SignData_ECDSA(); test_RSA_ToXMLStringEx(); test_CMS_MakeSigData_PSS(); test_MakeSignedEnveloped_PSS_OAEP(); test_PFX_MakeFile_PSS(); // New in [v12.1] test_CIPHER_EncryptAEAD(); test_X509_ReadCertFromP7Chain(); test_X509_ReadCertFromPFX(); } //******************** // THE TEST MODULES... //******************** static void test_MakeKeys() { /* Demonstrates: * Rsa.MakeKeys() * Ecc.MakeKeys() */ int r; Console.WriteLine("Generating two 2048-bit RSA keys..."); r = Rsa.MakeKeys("CA_RSA_2048.pub", "CA_RSA_2048.p8e", 2048, Rsa.PublicExponent.Exp_EQ_65537, 80, "password", Rsa.PbeOptions.Pbe_Pbkdf2_aes256_CBC, false); r = Rsa.MakeKeys("User_RSA_2048.pub", "User_RSA_2048.p8e", 2048, Rsa.PublicExponent.Exp_EQ_65537, 80, "password", Rsa.PbeOptions.Pbe_Pbkdf2_aes256_CBC, false); Console.WriteLine("Generating two ECC keys using P-256..."); r = Ecc.MakeKeys("CA_ECC_P256.pub", "CA_ECC_P256.p8e", Ecc.CurveName.P_256, "password", Ecc.PbeScheme.Pbe_Pbkdf2_aes256_CBC, "", 0); r = Ecc.MakeKeys("User_ECC_P256.pub", "User_ECC_P256.p8e", Ecc.CurveName.P_256, "password", Ecc.PbeScheme.Pbe_Pbkdf2_aes256_CBC, "", 0); } static void test_ReadKeys() { 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_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-----" + "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" + "-----END PKCS12-----"; // Check type of ASN.1 data s = Asn1.Type(pfxStr); Console.WriteLine("Data type is [{0}]", s); // Read in cert as base64 string certstr = X509.ReadCertStringFromPFX(pfxStr, "password"); // SECURITY!! Console.WriteLine("{0}", certstr); // Query the cert for subjectName s = X509.QueryCert(certstr, "subjectName"); Console.WriteLine("subjectName='{0}'", s); } // END TESTS //****************************************************** //***************** // FILE UTILITIES * //***************** 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; } //****************** // SETUP 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 = "STOPPED: Required file is missing.\n Look in '" + 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); } } //*********************** // DISPLAY ERROR DETAILS //*********************** /// <summary> /// Display details of last error given error code /// </summary> /// <param name="nErr">Error code returned</param> static void disp_error(int nErr) { string s = General.LastError(); Console.WriteLine("ERROR Returned={0}/{1}: {2}; {3}", nErr, General.ErrorCode(), General.ErrorLookup(nErr), s); } /// <summary> /// Display details of last error (no error code) /// </summary> static void disp_error() { string s = General.LastError(); int nErr = General.ErrorCode(); Console.WriteLine("ERROR {0}: {1}: {2}", nErr, General.ErrorLookup(nErr), s); } 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; } } }