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: 2006-08-09 08:39:00 $
* $Version: 2.9.0 $
*/
/* NOTE: This program requires the following files to exist in the
* same directory as the executable:
* CarlRSASelf.cer
* CarlRSASign.epk
* AliceRSASignByCarl.cer
* AlicePrivRSASign.epk
* BobRSASignByCarl.cer
* BobPrivRSAEncrypt.epk (NOTE: the .EPK extension is one we made up)
* bob.p7b
*/
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;
byte[] b;
bool isok;
byte[] msg;
byte[] bcheck;
StringBuilder sbPrivateKey;
StringBuilder sbPublicKey;
string keyhex, plain, cipher, 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;
string query;
//****************
// 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);
s = CryptoSysPKI.General.LastError();
Console.WriteLine("LastError='{0}' (expecting empty)", s);
n = CryptoSysPKI.General.PowerUpTests();
Console.WriteLine("PowerUpTests={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";
if (FileIsNotPresent("CarlRSASelf.cer", missingFile)) return;
if (FileIsNotPresent("CarlPrivRSASign.epk", missingFile)) return;
if (FileIsNotPresent("AliceRSASignByCarl.cer", missingFile)) return;
if (FileIsNotPresent("AlicePrivRSASign.epk", missingFile)) return;
if (FileIsNotPresent("BobRSASignByCarl.cer", missingFile)) return;
if (FileIsNotPresent("BobPrivRSAEncrypt.epk", missingFile)) return;
if (FileIsNotPresent("bob.p7b", missingFile)) return;
//*******************************************
// TDEA (Triple DES, 3DES) ENCRYPTION TESTS *
//*******************************************
Console.WriteLine("TESTING TRIPLE DES:");
keyhex = "010101010101010101010101010101010101010101010101";
plain = "8000000000000000";
cipher = "95F8A5E5DD31D900";
// Encrypt in ECB mode using hex strings
s = Tdea.Encrypt(plain, keyhex, Mode.ECB, null);
Console.WriteLine("KY={0}",keyhex);
Console.WriteLine("PT={0}",plain);
Console.WriteLine("CT={0}",s);
Console.WriteLine("OK={0}",cipher);
Debug.Assert(String.Compare(s, cipher, true)==0, "Tdea.HexECB failed");
// Decrypt
s = Tdea.Decrypt(cipher, keyhex, Mode.ECB, null);
Console.WriteLine("P'={0}",s);
Console.WriteLine("OK={0}",plain);
Debug.Assert(String.Compare(s, plain, true)==0, "Tdea.HexECB failed");
// Ditto using byte arrays
arrPlain = Cnv.FromHex(plain);
arrCipher = Cnv.FromHex(cipher);
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";
plain = "5468697320736F6D652073616D706520636F6E74656E742E0808080808080808";
cipher = "d76fd1178fbd02f84231f5c1d2a2f74a4159482964f675248254223daf9af8e4";
s = Tdea.Encrypt(plain, keyhex, Mode.CBC, ivhex);
Console.WriteLine("KY={0}",keyhex);
Console.WriteLine("IV={0}",ivhex);
Console.WriteLine("PT={0}",plain);
Console.WriteLine("CT={0}",s);
Console.WriteLine("OK={0}",cipher);
Debug.Assert(String.Compare(s, cipher, true)==0, "Tdea.Encrypt{Hex,CBC} failed");
// Decrypt
s = Tdea.Decrypt(cipher, keyhex, Mode.CBC, ivhex);
Console.WriteLine("P'={0}",s);
Console.WriteLine("OK={0}",plain);
Debug.Assert(String.Compare(s, plain, true)==0, "Tdea.Decrypt{Hex,CBC} failed");
// Ditto using byte arrays
arrPlain = Cnv.FromHex(plain);
arrCipher = Cnv.FromHex(cipher);
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);
Debug.Assert(String.Compare(s, sbPrivateKey.ToString().ToString()) == 0,
"Private key string from XML does not match original");
// Create a PKCS-12 file containing Alice's private key
fname = "alice.pfx";
n = Pfx.MakeFile(fname, "AliceRSASignByCarl.cer",
"AlicePrivRSASign.epk", "password", "Alice's Key", false);
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
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");
Debug.Assert(strCheck == 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();
sbPrivateKey = Rsa.ReadPrivateKeyInfo(fname);
Debug.Assert(strCheck == sbPrivateKey.ToString(), "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();
sbPublicKey = Rsa.ReadPublicKey(fname);
Debug.Assert(strCheck == sbPublicKey.ToString(), "Public keys don't match");
// Extract the certificate from a PKCS-12 PFX file
fnameInput = "alice.pfx";
fnameCert = "alice_fromPFX.cer";
n = X509.GetCertFromPFX(fnameCert, fnameInput);
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);
// 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);
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);
// 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);
}
//*****************************
// 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");
// 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, false);
// 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, false);
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, true);
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, false);
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, "", true);
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, "", "", false);
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, false);
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, false);
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, false);
Console.WriteLine("Cms.GetSigHashAlgorithm returns {0} (expecting -ve error code)", n);
Console.WriteLine("{0};{1}", General.ErrorLookup(n), General.LastError());
Debug.Assert(n < 0, "Cms.GetSigHashAlgorithm succeeded when should have failed!");
// Make a signed-data object file using MD5 and in base64 format
fnameOutput = "BasicSignByAlice_MD5.txt";
fnameInput = "excontent.txt";
fnameCert = "AliceRSASignByCarl.cer";
n = Cms.MakeSigData(fnameOutput, fnameInput, fnameCert, sbPrivateKey.ToString(),
Cms.Options.FormatBase64 | Cms.Options.UseMD5);
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, true);
Console.WriteLine("Cms.GetSigHashAlgorithm returns {0} (expecting 1 => MD5)", n);
Debug.Assert(1 == n, "Cms.GetSigHashAlgorithm failed");
// Query our SignedData files
fnameInput = "BasicSignByAlice_attr.bin";
query = "version";
s = Cms.QuerySigData(fnameInput, query, false);
Console.WriteLine("Cms.QuerySigData('{0}') returns '{1}'", query, s);
Debug.Assert(s.Length > 0, "QuerySigData failed");
query = "digestAlgorithm";
s = Cms.QuerySigData(fnameInput, query, false);
Console.WriteLine("Cms.QuerySigData('{0}') returns '{1}'", query, s);
Debug.Assert(s.Length > 0, "QuerySigData failed");
query = "signingTime";
s = Cms.QuerySigData(fnameInput, query, false);
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')={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);
//******************
// 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());
//*******************
// CONVERSION TESTS *
//*******************
// Convert an "impure" hex string of 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}')={1} ({2} bytes)", s, Cnv.ToHex(b), b.Length);
Debug.Assert(16==b.Length, "Cnv.FromHex failed: wrong # of bytes");
// Given a string that includes some Latin-1 accented characters:
s = "abcóéíáñ";
Console.WriteLine("Original Latin-1 string={0}", s);
Console.WriteLine("# of characters in Latin-1 string = {0}", s.Length);
// Check and see if this tests as valid UTF-8
n = Cnv.CheckUTF8(s);
Console.WriteLine("Cnv.CheckUTF8(s) returns {0} (expected 0 => not valid UTF-8)", n);
//************************
// PASSWORD PROMPT TESTS *
//************************
/* -- uncomment to test (these are tedious on repetitive tests!)
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("SOME RANDOM NUMBERS:");
for (i = 0; i < 3; i++)
{
b = Rng.Bytes(24);
Console.WriteLine("RNG={0}", Cnv.ToHex(b));
}
Console.Write("{x:990<=x<=1000}=");
for (i = 0; i < 5; i++)
{
n = Rng.Number(990, 1000);
Console.Write("{0} ", n);
}
Console.Write("\n");
Console.WriteLine("\nALL TESTS COMPLETED.");
}
//*****************
// FILE UTILITIES *
//*****************
static bool MakeATextFile(string fileName, string data)
{
FileStream fs;
StreamWriter sw;
// Create a test text file
fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
sw = new StreamWriter(fs);
sw.Write(data);
sw.Close();
fs.Close();
return true;
}
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 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 bool FileExists(string filePath)
{
FileInfo fi = new FileInfo(filePath);
return fi.Exists;
}
static bool FileIsNotPresent(string filePath, string message)
{
if (!FileExists(filePath))
{
Console.WriteLine("\n{0}: {1}", message, filePath);
return true;
}
return false;
}
public 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;
}
}
}