using System; using System.Diagnostics; using System.Reflection; using System.IO; using System.Text; using CryptoSysPKI; /* Some tests using the CryptoSysPKI .NET interface. */ /* $Id: TestPKIcsharp.cs $ * Last updated: * $Date: 2012-01-14 12:09:00 $ * $Version: 3.8.0 $ */ /* This is a Console Application written for target .NET Framework 2.0 and above */ /* NOTE: This program requires the following files to exist in the * same directory as the executable (these are in pkiDotNetTestFiles.zip): * CarlRSASelf.cer * CarlRSASign.epk * AliceRSASignByCarl.cer * AlicePrivRSASign.epk * BobRSASignByCarl.cer * BobPrivRSAEncrypt.epk (NOTE: the .EPK extension is one we made up) * bob.p7b * rfc3280bis_cert1.cer * rfc3280bis_cert2.cer * rfc3280bis_CRL.crl * dims.cer * UTNUSERFirst-Object.cer * ocsp_response_ok_dims.dat */ namespace TestPKIExamples { /// <summary> /// Test examples for CryptoSysPKI interface /// </summary> class TestPKIExamples { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main(string[] args) { string s; char ch; int i, n, nblock, r, k; byte[] b; bool isok; byte[] msg; byte[] bcheck; StringBuilder sbPrivateKey; StringBuilder sbPublicKey; string keyhex, plainStr, cipherStr, ivhex, okhex; byte[] arrPlain; byte[] arrCipher; byte[] arrKey; byte[] arrIV; string excontent, fnameData, fnameEnc, fnameCheck; string fnameInput, fnameOutput, fnameCert, fname; string pubkeyFile, prikeyFile; string issuerCert; string hexDigest; string strCheck, certBase64, distname; string query; StringBuilder sbKeyCheck; string cert1, cert2, certList, certFile, crlFile, snStr, csrFile; string extns, dn, password, xmlKey, msgStr, keyStr, dateStr; int keyBits; string subdir; string b64str, s1; byte[] b1; long flen; // Required test files string[] arrFileNames = new string[] { "AlicePrivRSASign.epk", "AliceRSASignByCarl.cer", "bob.p7b", "BobPrivRSAEncrypt.epk", "BobRSASignByCarl.cer", "CarlPrivRSASign.epk", "CarlRSASelf.cer", "rfc3280bis_cert1.cer", "rfc3280bis_cert2.cer", "rfc3280bis_CRL.crl", "dims.cer", "UTNUSERFirst-Object.cer", "ocsp_response_ok_dims.dat", }; // Handle arguments bool doPrompt = false; bool askDelete = false; bool doBigFile = false; for (int iarg = 0; iarg < args.Length; iarg++) { if (args[iarg] == "prompt") doPrompt = true; if (args[iarg] == "askdelete") askDelete = true; if (args[iarg] == "bigfile") doBigFile = true; } //**************** // GENERAL TESTS * //**************** Console.WriteLine("GENERAL FUNCTIONS:"); n = CryptoSysPKI.General.Version(); Console.WriteLine("Version={0}", n); ch = CryptoSysPKI.General.LicenceType(); Console.WriteLine("LicenceType={0}", ch); s = CryptoSysPKI.General.ModuleName(); Console.WriteLine("ModuleName={0}", s); s = CryptoSysPKI.General.CompileTime(); Console.WriteLine("CompileTime={0}", s); n = General.IsWin64(); Console.WriteLine("IsWin64={0}", n); 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); //************************************************** // 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 pkiDotNetTestFiles.zip"; foreach (string fn in arrFileNames) { if (FileIsNotPresent(fn, missingFile)) return; } //************************************************* // 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()); // Now do the tests... //******************************************* // TDEA (Triple DES, 3DES) ENCRYPTION TESTS * //******************************************* Console.WriteLine("TESTING 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"; MakeATextFile(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 = ReadABinaryFile(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 = ReadATextFile(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."); //************ // RSA TESTS * //************ Console.WriteLine("RSA FUNCTION TESTS:"); // Read the public key from the recipient's X.509 certificate sbPublicKey = Rsa.GetPublicKeyFromCert("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.ReadEncPrivateKey("AlicePrivRSASign.epk", "password"); Console.WriteLine("PrivateKey={0}", sbPrivateKey.ToString()); Console.WriteLine("PrivateKeyBits={0}", Rsa.KeyBits(sbPrivateKey.ToString().ToString())); Console.WriteLine("PrivateKeyBytes={0}", Rsa.KeyBytes(sbPrivateKey.ToString().ToString())); // Decrypt with private key b = Rsa.RawPrivate(b, sbPrivateKey.ToString().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().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().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 (CompareByteArrays(bcheck, b)) Console.WriteLine("OK, verification OK"); else Console.WriteLine("ERROR: verification failed"); Debug.Assert(CompareByteArrays(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(CompareByteArrays(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(CompareByteArrays(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(CompareByteArrays(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()); // Export key to XML format s = Rsa.ToXMLString(sbPrivateKey.ToString(), Rsa.XmlOptions.ForceRSAKeyValue); Console.WriteLine("Key in XML format={0}", s); // Test re-import from XML s = Rsa.FromXMLString(s, false); // [v3.0] This test does not work anymore: // the internal strings are always different even for the same key //Debug.Assert(String.Compare(s, sbPrivateKey.ToString()) == 0, // "Private key string from XML does not match original"); // 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.epk", "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.ReadEncPrivateKey(fnameOutput, "password"); strCheck = sbPrivateKey.ToString(); sbPrivateKey = Rsa.ReadEncPrivateKey("AlicePrivRSASign.epk", "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.ReadPrivateKeyInfo(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.ReadPrivateKeyFromPFX(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"); // Create a PKCS-12 file excluding Alice's private key; i.e. just the cert 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"); // Check it is an X.509 cert by getting its subject s = X509.CertSubjectName(fnameCert, ""); Console.WriteLine("{0} has subject {1}.", fnameCert, s); // [New v3.1] Show that a private and public key string are matched // Read in the private key from the encrypted key file sbPrivateKey = Rsa.ReadEncPrivateKey("AlicePrivRSASign.epk", "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.GetPublicKeyFromCert("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.GetPublicKeyFromCert("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"); // Generate a fresh RSA key which we'll use later pubkeyFile = "mykey_pub.bin"; prikeyFile = "mykey_epk.bin"; n = Rsa.MakeKeys(pubkeyFile, prikeyFile, 512, Rsa.PublicExponent.Exp_EQ_65537, 1024, "password", Rsa.PbeOptions.Default, 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"); //************* // X509 TESTS * //************* Console.WriteLine("X509 TESTS:"); // 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", X509.Options.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.epk"; n = X509.MakeCert(fnameCert, issuerCert, pubkeyFile, prikeyFile, 101, 2, "CN=Me;C=US;O=MyOrg", "", 0, "password", X509.Options.SigAlg_Md5WithRSAEncryption | X509.Options.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); // 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"); // [New v3.1] 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", 0); Console.WriteLine("X509.CertRequest returned {0} (expected 0).", n); // 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); Console.WriteLine("X509.GetCertFromP7Chain(0) returns {0} (expected 2).", n); // 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); } // [New v3.1] Bob creates a self-signed cert using his Chinese nickname, Ben fnameCert = "benChina.cer"; prikeyFile = "BobPrivRSAEncrypt.epk"; // 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", X509.Options.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 s = X509.CertSubjectName(fnameCert, ""); Console.WriteLine("Subject name is '{0}'", s); // Ben then creates a certificate for a Mexican user //(using the public key we created above and his chinese certificate!) issuerCert = "benChina.cer"; fnameCert = "mariaMexico.cer"; // passing the UTF-8 characters for the name in [v3.1] hex format // C=México;CN=María;OU=#xabc (yes, we want the OU to be "#xabc") distname = "C=#x4de97869636f;CN=#x4d6172ed61;OU=##xabc"; n = X509.MakeCert(fnameCert, issuerCert, pubkeyFile, prikeyFile, 7, 2, distname, "", 0, "password", X509.Options.UTF8String); Console.WriteLine("X509.MakeCertSelf(mexican) returned {0}", n); Debug.Assert(n == 0, "Failed to create new certificate"); // Now read in the Subject's name using QueryCert // forcing the name to be converted back to Latin-1 encoding s = X509.QueryCert(fnameCert, "subjectName", X509.Options.Latin1); Console.WriteLine("Subject's name is '{0}'", s); Debug.Assert(s.Length > 0, "Failed to read subjectName"); // [new in v3.3] 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 startng with it) pubkeyFile = "AlicePubRSA.pub"; sbPublicKey = Rsa.GetPublicKeyFromCert("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.epk"; 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, X509.Options.AuthKeyId); Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r); 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"); // [new in v3.5] Create a new certificate using a PKCS-10 certificate signing request (CSR) // instead of using a subjectPublicKey file and distininguished name. Console.WriteLine("Create a new certificate using a PKCS-10 CSR file..."); csrFile = "myreq.txt"; X509.TextDump("dump.txt", csrFile); PrintATextFile("dump.txt"); fnameCert = "mycert-fromCSR.cer"; issuerCert = "CarlRSASelf.cer"; prikeyFile = "CarlPrivRSASign.epk"; password = "password"; extns = "notBefore=2010-01-01T12:00"; // Make an end-user cert valid for 4 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, 4, "", extns, 0, password, 0); Console.WriteLine("X509.MakeCert returns {0} (expecting 0)", r); Debug.Assert(r == 0, "X509.MakeCert failed"); Console.WriteLine("Created cert file {0}", fnameCert); X509.TextDump("dump.txt", fnameCert); PrintATextFile("dump.txt"); // [new in v3.5] 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"); //****************************************** // CRL (CERTIFICATE REVOCATION LIST) TESTS * //****************************************** // [new in v3.5] 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.epk"; 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, "", 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, X509.Options.SigAlg_Sha256WithRSAEncryption); 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 = +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); Debug.Assert(r == 1, "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()); //************************************************** // OCSP (ONLINE CERTIFICATE STATUS PROTOCOL) TESTS * //************************************************** // [new in v3.5] Console.WriteLine("OCSP (ONLINE CERTIFICATE STATUS PROTOCOL) TESTS:"); // Creates an OCSP request to check our own current code signing certificate file dims.cer. // This was issued by the holder of certificate in the file UTNUSERFirst-Object.cer. issuerCert = "UTNUSERFirst-Object.cer"; certFile = "dims.cer"; Console.WriteLine("IssuerFile={0}", issuerCert); Console.WriteLine("CertFile={0}", certFile); s = Ocsp.MakeRequest(issuerCert, certFile, 0); Debug.Assert(s.Length > 0, "Ocsp.MakeRequest failed"); Console.WriteLine("OCSPRequest={0}", s); // Pass a hex serial number instead of filename snStr = "#x 00 FB C7 23 22 8C 8C 80 22 D8 85 92 23 DE E7 06 60"; Console.WriteLine("Cert SerialNumber={0}", snStr); s = Ocsp.MakeRequest(issuerCert, snStr, 0); Debug.Assert(s.Length > 0, "Ocsp.MakeRequest failed"); Console.WriteLine("OCSPRequest={0}", s); // We use a response received from ocsp.usertrust.com for our own code signing certificate // (this HAS been signed by our cert's issuer) fname = "ocsp_response_ok_dims.dat"; issuerCert = "UTNUSERFirst-Object.cer"; Console.WriteLine("ResponseFile={0}", fname); Console.WriteLine("IssuerFile={0}", issuerCert); s = Ocsp.ReadResponse(fname, issuerCert); Debug.Assert(s.Length > 0, "Ocsp.ReadResponse failed"); Console.WriteLine("OCSPResponse={0}", s); //***************************** // CMS (S/MIME OBJECTS) TESTS * //***************************** Console.WriteLine("CMS (S/MIME OBJECTS) TESTS:"); // Create a test text file fnameInput = "excontent.txt"; MakeATextFile(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, 0); 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.ReadEncPrivateKey("BobPrivRSAEncrypt.epk", "password"); Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key"); n = Cms.ReadEnvDataToFile(fnameOutput, fnameInput, "", sbPrivateKey.ToString(), 0); Console.WriteLine("Cms.ReadEnvData returns {0} (expecting 0)", n); Debug.Assert(0 == n, "Cms.ReadEnvData failed"); s = ReadATextFile(fnameOutput); Console.WriteLine("MSG={0}", s); // and again but read directly into a string s = Cms.ReadEnvDataToString(fnameInput, "", sbPrivateKey.ToString(), 0); 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, Cms.Options.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.Options.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.ReadEncPrivateKey("AlicePrivRSASign.epk", "password"); fnameOutput = "BasicSignByAlice.bin"; fnameInput = "excontent.txt"; fnameCert = "AliceRSASignByCarl.cer"; n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(), 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 = ReadATextFile(fnameInput); fname = "BasicSignByAlice1.bin"; n = Cms.MakeSigDataFromString(fname, s, fnameCert, sbPrivateKey.ToString(), 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 = ReadABinaryFile(fnameOutput); bcheck = ReadABinaryFile(fname); Debug.Assert(CompareByteArrays(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(), 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 = ReadATextFile(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.Options.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.Options.IncludeAttributes|Cms.Options.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 // [v3.7] new option for HashAlgorithm fnameOutput = "BasicSignByAlice_MD5.txt"; fnameInput = "excontent.txt"; fnameCert = "AliceRSASignByCarl.cer"; n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(), HashAlgorithm.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"); // Added new overload v3.7 [2011-06-27] // 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(), HashAlgorithm.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 = ReadATextFile("excontent.txt"); fname = "BasicSignByAlice_224.bin"; n = Cms.MakeSigDataFromString(fname, s, fnameCert, sbPrivateKey.ToString(), HashAlgorithm.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.Options.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. //************* // HASH TESTS * //************* Console.WriteLine("HASH DIGEST TESTS:"); s = Hash.HexFromString("abc", HashAlgorithm.Sha1); Console.WriteLine("SHA-1('abc') ={0}", s); s = Hash.HexFromString("abc", HashAlgorithm.Md5); Console.WriteLine("MD5('abc') ={0}", s); s = Hash.HexFromString("abc", HashAlgorithm.Md2); Console.WriteLine("MD2('abc') ={0}", s); s = Hash.HexFromString("abc", HashAlgorithm.Sha256); Console.WriteLine("SHA-256('abc')=\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 fnameInput = "hello.txt"; MakeATextFile(fnameInput, "hello world\r\n"); // get digest from binary file s = Hash.HexFromFile(fnameInput, 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(fnameInput, HashAlgorithm.Sha1); Console.WriteLine("SHA1('hello world+LF')= {0}", s); //************* // HMAC TESTS * //************* Console.WriteLine("HMAC TESTS:"); // Test case 2 from RFC 2202 and RFC 4231 // key = "Jefe" // key_len 4 // data = "what do ya want for nothing?" // data_len = 28 Console.WriteLine("Test case 2 from RFC 2202 and RFC 4231..."); // Convert strings to byte arrays arrKey = 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, arrKey, 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, arrKey, 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..."); arrKey = new byte[25]; for (i = 0; i < 25; i++) arrKey[i] = (byte)(i+1); Console.WriteLine("Key={0}", Cnv.ToHex(arrKey)); 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, arrKey, 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, arrKey, 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"); //****************** // WIPE DATA TESTS * //****************** Console.WriteLine("WIPE DATA TESTS:"); fname = "ImportantSecret.txt"; MakeATextFile(fname, "Very important secrets here."); s = ReadATextFile(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)); Console.WriteLine("Before Wipe.String, sbPrivateKey contains {0} characters.", sbPrivateKey.Length); Wipe.String(sbPrivateKey); Console.WriteLine("After Wipe.String, sbPrivateKey = [{0}]", sbPrivateKey.ToString()); //************************ // PASSWORD PROMPT TESTS * //************************ 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); } //************ // RNG TESTS * //************ Console.WriteLine("GENERATE 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]; MakeABinaryFile(fname, b); } } isok = Rng.Initialize(fname); Console.WriteLine("Rng.Initialize('{0}') returns {1} (expecting 0)", 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 0)", 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 * //**************************************** Console.WriteLine("TESTING CIPHER(tdea, hex):"); // Encrypt in CBC mode using hex strings keyhex = "737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32"; ivhex = "B36B6BFB6231084E"; plainStr = "5468697320736F6D652073616D706520636F6E74656E742E0808080808080808"; cipherStr = "d76fd1178fbd02f84231f5c1d2a2f74a4159482964f675248254223daf9af8e4"; s = Cipher.Encrypt(plainStr, keyhex, ivhex, CipherAlgorithm.Tdea, Mode.CBC); Console.WriteLine("KY={0}",keyhex); Console.WriteLine("IV={0}",ivhex); Console.WriteLine("PT={0}",plainStr); Console.WriteLine("CT={0}",s); Console.WriteLine("OK={0}",cipherStr); Debug.Assert(String.Compare(s, cipherStr, true)==0, "Cipher.Encrypt{Hex,tdea-CBC} failed"); // Decrypt s = Cipher.Decrypt(cipherStr, keyhex, ivhex, CipherAlgorithm.Tdea, Mode.CBC); Console.WriteLine("P'={0}",s); Console.WriteLine("OK={0}",plainStr); Debug.Assert(String.Compare(s, plainStr, true)==0, "Cipher.Decrypt{Hex,tdea-CBC} failed"); Console.WriteLine("TESTING CIPHER(aes128, hex):"); // AES using hex strings keyhex = "0123456789ABCDEFF0E1D2C3B4A59687"; ivhex = "FEDCBA9876543210FEDCBA9876543210"; // "Now is the time for all good men" plainStr = "4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E"; cipherStr = "c3153108a8dd340c0bcb1dfe8d25d2320ee0e66bd2bb4a313fb75c5638e9e177"; s = Cipher.Encrypt(plainStr, keyhex, ivhex, CipherAlgorithm.Aes128, Mode.CBC); Console.WriteLine("KY={0}",keyhex); Console.WriteLine("IV={0}",ivhex); Console.WriteLine("PT={0}",plainStr); Console.WriteLine("CT={0}",s); Console.WriteLine("OK={0}",cipherStr); Debug.Assert(String.Compare(s, cipherStr, true)==0, "Cipher.Encrypt{Hex,aes128-CBC} failed"); // Decrypt s = Cipher.Decrypt(cipherStr, keyhex, ivhex, CipherAlgorithm.Aes128, Mode.CBC); Console.WriteLine("P'={0}",s); Console.WriteLine("OK={0}",plainStr); Debug.Assert(String.Compare(s, plainStr, true)==0, "Cipher.Decrypt{Hex,tdea-CBC} failed"); // Test some errors Console.WriteLine("TESTING CIPHER ERRORS:"); Console.WriteLine("Test an invalid key..."); s = Cipher.Encrypt(plainStr, "GARBAGEKEY", "", CipherAlgorithm.Tdea, Mode.ECB); Console.Write("Cipher.Encrypt returns '{0}' ",s); n = General.ErrorCode(); Console.WriteLine("ErrorCode={0} {1}", n, General.ErrorLookup(n)); Console.WriteLine("Test an invalid IV..."); s = Cipher.Encrypt(plainStr, keyhex, "DEADIV", CipherAlgorithm.Tdea, 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.Tdea, Mode.ECB); 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."); // ADVANCED PRIVATE KEY SAVING OPTIONS - NEW OVERLOAD for Rsa.SaveEncPrivateKey // Read in a known private key from its encrypted key file Console.WriteLine("\nTEST RSA SAVE ENCRYPTED PRIVATE KEY OPTIONS:"); Console.WriteLine("Read in a private key..."); sbPrivateKey = Rsa.ReadEncPrivateKey("AlicePrivRSASign.epk", "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.ReadEncPrivateKey(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.ReadEncPrivateKey(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.ReadEncPrivateKey(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"); Console.WriteLine("\nHASH DIGEST TEST FOR SHA-224:"); // Hash of "abc" with input in hex format 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"); Console.WriteLine("\nMORE 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"); Console.WriteLine(""); Console.WriteLine("ADVANCED ENVELOPED-DATA OBJECTS:"); // Create an enveloped CMS object Alice to Bob using Bob's X.509 certificate fname = "cms2bob_aes128.p7m"; fnameCert = "BobRSASignByCarl.cer"; // This should return 1 (indicating one successful recipient) s = "This is some sample content."; n = Cms.MakeEnvDataFromString(fname, s, fnameCert, CipherAlgorithm.Aes128, Cms.KeyEncrAlgorithm.Rsa_Pkcs1v1_5, 0, 0); Console.WriteLine("Cms.MakeEnvDataFromString returns {0} (expecting 1)", n); Debug.Assert(1 == n, "Cms.MakeEnvDataFromString failed"); // Query the enveloped-data file query = "keyEncryptionAlgorithm"; s = Cms.QueryEnvData(fname, query, false); Console.WriteLine("{0}='{1}'", query, s); query = "contentEncryptionAlgorithm"; s = Cms.QueryEnvData(fname, query, false); Console.WriteLine("{0}='{1}'", query, s); // Now try and read it using Bob's private key sbPrivateKey = Rsa.ReadEncPrivateKey("BobPrivRSAEncrypt.epk", "password"); Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key"); s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString(), 0); Console.WriteLine("MSG={0}", s); Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed"); /* [RSA-KEM withdrawn in v3.4] * -----BEGIN WITHDRAWN CODE----- // Repeat using RSA-KEM with AES-192 and SHA-256 with output in base 64 fname = "cms2bob_kem_a196s256.p7m.txt"; fnameCert = "BobRSASignByCarl.cer"; // This should return 1 (indicating one successful recipient) s = "This is some sample content."; n = Cms.MakeEnvDataFromString(fname, s, fnameCert, CipherAlgorithm.Aes192, Cms.KeyEncrAlgorithm.Rsa_Kem, HashAlgorithm.Sha256, Cms.EnvDataOptions.FormatBase64); Console.WriteLine("Cms.MakeEnvDataFromString returns {0} (expecting 1)", n); Debug.Assert(1 == n, "Cms.MakeEnvDataFromString failed"); // Query the enveloped-data file (note flag for isBase64) query = "keyEncryptionAlgorithm"; s = Cms.QueryEnvData(fname, query, true); Console.WriteLine("{0}='{1}'", query, s); query = "contentEncryptionAlgorithm"; s = Cms.QueryEnvData(fname, query, true); Console.WriteLine("{0}='{1}'", query, s); // Now try and read it using Bob's private key sbPrivateKey = Rsa.ReadEncPrivateKey("BobPrivRSAEncrypt.epk", "password"); Debug.Assert(sbPrivateKey.ToString().Length > 0, "Unable to read Bob's private key"); s = Cms.ReadEnvDataToString(fname, "", sbPrivateKey.ToString(), Cms.Options.FormatBase64); Wipe.String(sbPrivateKey); Console.WriteLine("MSG={0}", s); Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed"); * -----END WITHDRAWN CODE----- */ Console.WriteLine(""); Console.WriteLine("STRONGER RSA KEY ENCRYPTION:"); // Read in Carl's key fname = "CarlPrivRSASign.epk"; Console.WriteLine("Reading private key file {0}", fname); sbPrivateKey = Rsa.ReadEncPrivateKey(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.epk.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.ReadEncPrivateKey(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); // **** [2008-05-03] WE MESSED UP THE FLAGS FOR PKI_KEYGEN_INDICATE and PKI_BC_TDEA Console.WriteLine("GENERATE NEW RSA KEY PAIR WITH STRONGER ENCRYPTION - TEST FUDGE FOR TDEA:"); // 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, 512, Rsa.PublicExponent.Exp_EQ_65537, 3000, "password", 0, HashAlgorithm.Sha1, Rsa.Format.Binary, false); 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.ReadEncPrivateKey(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, 768, Rsa.PublicExponent.Exp_EQ_65537, 3000, "password", CipherAlgorithm.Aes128, HashAlgorithm.Sha224, 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.ReadEncPrivateKey(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(""); Console.WriteLine("STRONGER X.509 SIGNATURE ALGORITHMS:"); // Create a new self-signed CA certificate for Carol 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", X509.Options.SigAlg_Sha384WithRSAEncryption); 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.GetPublicKeyFromCert("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", X509.Options.SigAlg_Sha256WithRSAEncryption); 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(""); Console.WriteLine("MAKE ENVELOPED-DATA USING PEM-STYLE STRINGS FOR CERTS:"); // First, we'll re-save a couple of X.509 certificates in PEM format, // then we'll read these PEM files (which are text) into strings. fname = "CarolSelf_384.cer"; s = X509.ReadStringFromFile(fname); fname = fname+".pem.txt"; n = X509.SaveFileFromString(fname, s, true); Console.WriteLine("Created {0}", fname); cert1 = ReadATextFile(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 = ReadATextFile(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, false); Console.WriteLine("{0}='{1}'", query, s); query = "countOfRecipientInfos"; s = Cms.QueryEnvData(fname, query, false); 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, false); Console.WriteLine("{0}='{1}'", query, s); query = String.Concat("recipientSerialNumber", "/", i); s = Cms.QueryEnvData(fname, query, false); 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) Console.WriteLine("Reading file '{0}'", prikeyFile); s = ReadATextFile(prikeyFile); // so instead of using the filename, we can read this string instead // [new feature v3.2] sbPrivateKey = Rsa.ReadEncPrivateKey(s, "password"); Debug.Assert(sbPrivateKey.Length > 0, "Rsa.ReadEncPrivateKey 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(), 0); Debug.Assert(s.Length > 0, "Cms.ReadEnvDataToString failed."); Console.WriteLine("Msg='{0}'", s); // Clean up Wipe.String(sbPrivateKey); // ************** // KEY WRAPPING // ************** Console.WriteLine(""); Console.WriteLine("KEY WRAPPING WITH BLOCK CIPHER:"); // Some test AES-128 key material data arrPlain = Cnv.FromHex("00112233 44556677 8899aabb ccddeeff"); Console.WriteLine("Key material={0}", Cnv.ToHex(arrPlain)); // Key Encryption Key arrKey = Cnv.FromHex("c17a44e8 e28d7d64 81d1ddd5 0a3b8914"); Console.WriteLine("KEK ={0}", Cnv.ToHex(arrKey)); // Wrap with AES128-Wrap algorithm Console.WriteLine("Using AES128-Wrap"); arrCipher = Cipher.KeyWrap(arrPlain, arrKey, CipherAlgorithm.Aes128); Debug.Assert(arrCipher.Length > 0, "Cipher.KeyWrap failed."); Console.WriteLine("Wrapped Key ={0}", Cnv.ToHex(arrCipher)); // Check we can unwrap it bcheck = Cipher.KeyUnwrap(arrCipher, arrKey, CipherAlgorithm.Aes128); Debug.Assert(arrCipher.Length > 0, "Cipher.KeyUnwrap failed."); Console.WriteLine("Check ={0}", Cnv.ToHex(bcheck)); Debug.Assert(CompareByteArrays(bcheck, arrPlain), "Unwrapped key is different."); /* [RSA-KEM withdrawn in v3.4] * -----BEGIN WITHDRAWN CODE----- Console.WriteLine(""); Console.WriteLine("KEY WRAPPING WITH RSA-KEM:"); // We use the same key material Console.WriteLine("Key material={0}", Cnv.ToHex(arrPlain)); // but this time wrap using RSA-KEM with Bob's public key fnameCert = "BobRSASignByCarl.cer"; Console.WriteLine("Reading public key from '{0}'", fnameCert); sbPublicKey = Rsa.GetPublicKeyFromCert(fnameCert); Debug.Assert(sbPublicKey.Length > 0, "Rsa.GetPublicKeyFromCert failed."); Console.WriteLine("Bob's public key length={0} bits", Rsa.KeyBits(sbPublicKey.ToString())); Console.WriteLine("Using RSA-KEM with AES128 and SHA-224"); arrCipher = Rsa.KemWrap(arrPlain, sbPublicKey.ToString(), CipherAlgorithm.Aes128, HashAlgorithm.Sha224); Debug.Assert(arrCipher.Length > 0, "Rsa.KemWrap failed."); Console.WriteLine("Wrapped Key ={0}", Cnv.ToHex(arrCipher)); // Check we can unwrap it using bob's private key fname = "BobPrivRSAEncrypt.epk"; sbPrivateKey = Rsa.ReadEncPrivateKey(fname, "password"); Debug.Assert(sbPrivateKey.Length > 0, "Rsa.ReadEncPrivateKey failed."); bcheck = Rsa.KemUnwrap(arrCipher, sbPrivateKey.ToString(), CipherAlgorithm.Aes128, HashAlgorithm.Sha224); Debug.Assert(arrCipher.Length > 0, "Rsa.KemUnwrap failed."); Console.WriteLine("Check ={0}", Cnv.ToHex(bcheck)); Debug.Assert(CompareByteArrays(bcheck, arrPlain), "Unwrapped key is different."); * -----END WITHDRAWN CODE----- */ // ********************* // AUTACK SIGNATURES // ********************* // (This is specialist stuff) Console.WriteLine(""); Console.WriteLine("AUTACK SIGNATURES:"); /* Ref: EDIFACT, Section 12.9 Worked Examples */ /* Short RSA private key in XML format -- this is NOT SECURE! */ xmlKey = "<RSAKeyPair>"+ "<Modulus EncodingType='hexBinary'>" + "A59FBFE322244760E5B430B197967FDCF240D6B134D0F3783EDE652B565AC9C6" + "105768F11EE59AED359ED6DB6CE6AFC84233F35B60895B90AF85A66E598C15CE" + "FB860EA37CCEDB4A45B4C0594974EB76BC955C43B56B17940DFDCAB3E0C03F49" + "A308835772405E74085BF59FBA726969CBE348DAB6F9456DA57B40B64E6E3C65</Modulus>" + "<Exponent EncodingType='hexBinary'>010001</Exponent>" + "<D EncodingType='hexBinary'>" + "244FFDD97DED0FD4441089638A7D85FBAA86823BB87D7E7FF4E2BC322FFCF843" + "AB660ABD60DD8CE5E8AD72648A001AF6B06324FE3A106B89B19DFF232F116A5F" + "4C7151C38D4A132E0EEBC55EC0DC44EBE0CCBA8FCACB6C93F32997DAD8B9ACEA" + "B4BEC5D4A1F38A2218337C8C92D301C433A7E02EB4A456F5DE83AE1AD76E3D31</D>" + "</RSAKeyPair>"; msgStr = "abc"; // The correct answer strCheck = "4897C41FFCB27C4B77F0711890C5C48E9C42AE5A1548E1A4653CDF444C60350F" + "635A16393D5862DCBD83EF3727435B750CE889EB3C48C02EA0B14F6F6B4BA0D1" + "E16A010D42830110AB36AB183F2976B784656D4272A6215A44EAA504610C59AC" + "C615E661BE4EC5ACE09B8D9DCE165F0CE71AE8743266ED2F20F35862B3C9252D"; keyStr = Rsa.FromXMLString(xmlKey, false); keyBits = Rsa.KeyBits(keyStr); Console.WriteLine("Private key is {0} bits long", keyBits); // Compute message digest of data hexDigest = Hash.HexFromString(msgStr, HashAlgorithm.Sha1); Console.WriteLine("Message-to-encode=Digest={0}", hexDigest); // Convert digest to byte array msg = Cnv.FromHex(hexDigest); // Encode digest using ISO-9796-1 b = Rsa.EncodeMsgIso9796(msg, keyBits); Debug.Assert(b.Length > 0, "Failed to Encode for ISO9796-1"); Console.WriteLine("Encoded block=\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"); // ********************* // PEM FILE CONVERSIONS // ********************* Console.WriteLine(""); Console.WriteLine("PEM FILE CONVERSIONS:"); fnameInput = "AliceRSASignByCarl.cer"; fnameOutput = "AliceRSASignByCarl.pem.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 = ReadATextFile(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"); // 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"); // ************************* // PADDING FOR BLOCK CIPHERS // ************************* Console.WriteLine(""); Console.WriteLine("PADDING 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); //************************** // BASE64 CONVERSION TESTS * //************************** // New in [v3.6] Console.WriteLine(""); Console.WriteLine("BASE64 CONVERSION TESTS:"); // 0xFEDCBA9876543210 in base64 b64str = "/ty6mHZUMhA="; b = Cnv.FromBase64(b64str); Console.WriteLine("'{0}'=0x{1}", b64str, Cnv.ToHex(b)); // Back to base64 s = Cnv.ToBase64(b); Console.WriteLine("In base64='{0}'", s); // Directly to hex Console.WriteLine("In hex='{0}'", Cnv.HexFromBase64(b64str)); // hex -> base64 s = "FEDCBA9876543210"; b64str = Cnv.Base64FromHex(s); Console.WriteLine("0x{0}='{1}'", s, b64str); // string -> base64 s = "México"; b64str = Cnv.ToBase64(s); // base64 -> hex Console.WriteLine("'{0}'=>'{1}'", s, b64str); Console.WriteLine("In hex='{0}'", Cnv.HexFromBase64(b64str)); // base64 -> string s = Cnv.StringFromBase64(b64str); Console.WriteLine("'{0}'=>'{1}'", b64str, s); // Filter out non-base64 chars (%.!*&) and e-acute s = "%/ty6m.!*HZUMhA=&é"; 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"); //******************* // CONVERSION TESTS * //******************* Console.WriteLine(""); Console.WriteLine("OTHER CONVERSION TESTS:"); // Create a string that includes some Latin-1 accented characters: s = "abcóéíáñ"; // This should contain the following 8 characters: // U+0061 LATIN SMALL LETTER A // U+0062 LATIN SMALL LETTER B // U+0063 LATIN SMALL LETTER C // U+00F3 LATIN SMALL LETTER O WITH ACUTE // U+00E9 LATIN SMALL LETTER E WITH ACUTE // U+00ED LATIN SMALL LETTER I WITH ACUTE // U+00E1 LATIN SMALL LETTER A WITH ACUTE // U+00F1 LATIN SMALL LETTER N WITH TILDE Console.WriteLine("Use GetBytes to convert a string to bytes..."); Console.WriteLine("Test string='{0}' ({1} chars)", s, s.Length); Debug.Assert(8 == s.Length, "String must be 8 chars for these tests"); Console.WriteLine("Use GetBytes in 'Default' mode:"); b = System.Text.Encoding.Default.GetBytes(s); Console.WriteLine("Default.GetBytes=>{0} ({1} bytes)", Cnv.ToHex(b), b.Length); Console.WriteLine("Use GetBytes in 'Latin-1' mode. Three alternatives:"); b = System.Text.Encoding.GetEncoding("iso-8859-1").GetBytes(s); Console.WriteLine("GetEncoding(\"iso-8859-1\").GetBytes=>{0} ({1} bytes)", Cnv.ToHex(b), b.Length); // Code page 28591 == iso-8859-1 b = System.Text.Encoding.GetEncoding(28591).GetBytes(s); Console.WriteLine("GetEncoding(28591).GetBytes=>{0} ({1} bytes)", Cnv.ToHex(b), b.Length); // Note: Windows-1252 is *almost* the same as ISO-8859-1 b = System.Text.Encoding.GetEncoding(1252).GetBytes(s); Console.WriteLine("GetEncoding(1252).GetBytes =>{0} ({1} bytes)", Cnv.ToHex(b), b.Length); Console.WriteLine("But in UTF-8 the bytes are different:"); b = System.Text.Encoding.UTF8.GetBytes(s); Console.WriteLine("UTF8.GetBytes=>{0} ({1} bytes)", Cnv.ToHex(b), b.Length); // Check that byte array contains valid UTF-8 characters b = System.Text.Encoding.UTF8.GetBytes(s); n = Cnv.CheckUTF8(b); Console.WriteLine("Cnv.CheckUTF8(b)={0} (expected 2)", n); Debug.Assert(2 == n, "Expected return value 2"); // Test a file instead of a byte array [New in v3.7] fname = "test-utf8.txt"; MakeABinaryFile(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)); // CONVERT OUR BYTE ARRAY FROM THE TEST STRING BACK TO A STRING TYPE Console.WriteLine("Use GetString to convert bytes back to a string..."); // b contains test string encoded into UTF-8 bytes // First we do the right thing s1 = System.Text.Encoding.UTF8.GetString(b); Console.WriteLine("UTF8.GetString(b)='{0}' (should display OK with accents and tilde)", s1); n = Cnv.CheckUTF8(s); Console.WriteLine("Cnv.CheckUTF8(s) returns {0} (expected 0 => not valid UTF-8)", n); // Then we force "UTF-8" bytes into a "Latin-1" string which will print "funny" s1 = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(b); Console.WriteLine("(\"iso-8859-1\").GetString(b)='{0}' (should show 'funny' characters)", s1); n = Cnv.CheckUTF8(s); Console.WriteLine("Cnv.CheckUTF8(string) returns {0} (expected 0=>invalid)", n); n = Cnv.CheckUTF8(b); Console.WriteLine("Cnv.CheckUTF8(bytes) returns {0} (expected 2 => Valid UTF-8 w/8-bit ANSI)", n); // 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)); /* HEX CONVERSIONS */ Console.WriteLine("Show the Cnv.FromHex method will filter out non-hex characters..."); // Convert a hex string containing ":" 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"); // ******************************************** // FINALLY, PRINT OUT DETAILS OF CORE DLL AGAIN // ******************************************** Console.WriteLine(""); Console.WriteLine("Details of core DLL..."); n = General.Version(); ch = General.LicenceType(); s = General.Platform(); Console.WriteLine("Version={0}, LicenceType={1}, Platform={2}", n, ch, s); s = General.ModuleName(); Console.WriteLine("ModuleName={0}", s); s = General.CompileTime(); Console.WriteLine("CompileTime={0}", s); //******************************************** Console.WriteLine("\nALL TESTS COMPLETED."); //********************************************************* // Put CWD back to parent and offer to remove the test dir //********************************************************* 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); } } //***************** // FILE UTILITIES * //***************** static bool MakeATextFile(string fileName, string data) { FileStream fs; StreamWriter sw; fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); sw = new StreamWriter(fs); sw.Write(data); sw.Close(); fs.Close(); return true; } static bool MakeABinaryFile(string fileName, byte[] data) { FileStream fs; BinaryWriter bw; fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); bw = new BinaryWriter(fs); bw.Write(data); bw.Close(); fs.Close(); return true; } 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 byte[] ReadABinaryFile(string fileName) { byte[] b = new byte[0]; FileInfo finfo = new FileInfo(fileName); if (finfo.Exists) { FileStream fsi = finfo.OpenRead(); BinaryReader br = new BinaryReader(fsi); int count = (int)fsi.Length; b = br.ReadBytes(count); br.Close(); fsi.Close(); } Debug.Assert(finfo.Exists, "File '" + fileName + "' does not exist."); return b; } static string ReadATextFile(string fileName) { string s = String.Empty; FileInfo finfo = new FileInfo(fileName); if (finfo.Exists) { FileStream fsi = finfo.OpenRead(); StreamReader sr = new StreamReader(fsi); s = sr.ReadToEnd(); //Console.WriteLine(s); sr.Close(); fsi.Close(); } Debug.Assert(finfo.Exists, "File '" + fileName + "' does not exist."); return s; } static string PrintATextFile(string fileName) { // Same as ReadATextFile except it prints to the console string s = String.Empty; FileInfo finfo = new FileInfo(fileName); if (finfo.Exists) { FileStream fsi = finfo.OpenRead(); StreamReader sr = new StreamReader(fsi); s = sr.ReadToEnd(); Console.WriteLine(s); sr.Close(); fsi.Close(); } return s; } 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 CompareByteArrays (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; } } }