// $Id: GermanHealthSetup.cs $ /******************************************************************************* ' Copyright (c) 2014 DI Management Services Pty Limited. All rights reserved. ' Provided to illustrate the use of functions in the CryptoSys PKI Toolkit. ' Not necessarily a good example of secure programming techniques. ' Provided "as is" with no warranties. Use at your own risk. ' *****************************************************************************/ using System; using System.Text; using System.IO; using CryptoSysPKI; namespace GermanHealthSetup { class GermanHealthSetup { private const string YOUR_PKID = "999009999"; static void Main(string[] args) { int ver = General.Version(); Console.WriteLine("PKI Version={0}", ver); if (ver < 31000) { Console.WriteLine("Require CryptoSys PKI version 3.10.0 or greater"); return; } //Console.WriteLine("Number of command line parameters = {0}", args.Length); //foreach (string s in args) //{ // System.Console.WriteLine(s); //} // Expecting at least one argument if (args.Length < 1) usage(); Console.WriteLine("Doing " + args[0] + "..."); // Act on first command-line argument switch (args[0]) { case "makekeys": makekeys(); break; case "makecertreq": makecertreq(); break; case "makedigest": makedigest(); break; case "makecert": makecert(); break; case "makecertchain": makecertchain(); break; case "splitp7c": splitp7c(); break; case "findourcert": findourcert(); break; case "validatep7c": validatep7c(); break; default: usage(); break; } } static void usage() { Console.WriteLine("ERROR: Expecting command: 'makekeys', 'makecertreq',..."); System.Environment.Exit(1); } static void display_error(int code) { Console.WriteLine("ERROR: " + General.ErrorLookup(code) + ": " + General.LastError()); } /// <summary> /// Create a new pair of 2048-bit RSA private/public keys saved as binary BER-encoded files /// </summary> /// <returns></returns> static bool makekeys() { // Do this just once. // NOTE: each time you run this you destroy the earlier keys // and need to run all subsequent procedures again // Set filenames to be created string pubKeyFile = YOUR_PKID + "_pub.p1"; string priKeyFile = YOUR_PKID + "_pri.p8"; // Set your password - use something stronger! // DO NOT HARDCODE PRODUCTION PASSWORDS! string password = "password"; // Create a new pair of RSA keys int n = Rsa.MakeKeys(pubKeyFile, priKeyFile, 2048, Rsa.PublicExponent.Exp_EQ_65537, 2000, password, Rsa.PbeOptions.Default, true); if (n == 0) Console.WriteLine("Created files: '" + pubKeyFile + "' and '" + priKeyFile + "'"); else display_error(n); return (0 == n); } /// <summary> /// Create a Certificate Request file (CRS, PKCS#10, .p10 file) /// </summary> /// <returns></returns> static bool makecertreq() { // Name of file to be created string reqFile = YOUR_PKID + ".p10"; // Existing key file and password // DO NOT HARDCODE PRODUCTION PASSWORDS! string priKeyFile = YOUR_PKID + "_pri.p8"; string password = "password"; // Distinguished name, in correct order string distName = "C=DE;O=ISTG Beispiel Vertrauen Mitte;OU=Unsere Firma;OU=IK999009991;CN=Erika Mustermann"; // Create certificate request, signing with sha256RSA int n = X509.CertRequest(reqFile, priKeyFile, distName, password, X509.Options.SigAlg_Sha256WithRSAEncryption); if (n == 0) Console.WriteLine("Created file '" + reqFile + "'"); else display_error(n); return (0 == n); } /// <summary> /// Compute the SHA-1 message digest value of the public key /// </summary> /// <returns></returns> static bool makedigest() { // Compute the SHA-1 digest of the public key file created earlier string pubKeyFile = YOUR_PKID + "_pub.p1"; string digestHex = Hash.HexFromFile(pubKeyFile, HashAlgorithm.Sha1); if (digestHex.Length == 0) display_error(General.ErrorCode()); Console.WriteLine("SHA1(PublicKey)=" + digestHex); return (digestHex.Length > 0); } /// <summary> /// Create an X.509 certificate from the p10 file sent by the user signed by the intermediate CA /// </summary> /// <remarks>You cannot do this. It is done by the CA using its own private key. /// We do it here to get a dummy certificate in the correct format to use</remarks> /// <returns></returns> static bool makecert() { // Cert file to be created string certFile = YOUR_PKID + ".cer"; // Input string reqFile = YOUR_PKID + ".p10"; string issuerCert = "Int_Cert.cer"; int certNum = 0x9999; // This is the CA's keyfile password, which you don't have string issuerKey = "Int_pri.p8"; string password = "password"; // Create new cert from certificate request file signed using sha256rsa int n = X509.MakeCert(certFile, issuerCert, reqFile, issuerKey, certNum, 7, "", "", 0, password, X509.Options.SigAlg_Sha256WithRSAEncryption); if (n == 0) Console.WriteLine("Created file '" + certFile + "'"); else display_error(n); return (0 == n); } /// <summary> /// Make a p7c "certs-only" signed-data chain file for user /// </summary> /// <remarks>This is done by the CA, not by you (although you could). /// We do it here to create a dummy file in the correct format you would receive from the CA. /// </remarks> /// <returns></returns> static bool makecertchain() { string outFile = YOUR_PKID + ".p7c"; string certList = YOUR_PKID + ".cer" + ";" + "Int_Cert.cer" + ";" + "CA_Cert.cer"; Console.WriteLine("certList=" + certList); // Create a certs-only .p7c chain int n = Cms.MakeSigData(outFile, "", certList, "", Cms.Options.CertsOnly); if (n == 0) Console.WriteLine("Created file '" + outFile + "'"); else display_error(n); return (0 == n); } /// <summary> /// Split the p7c cert list file into separate X.509 certificates /// </summary> /// <returns></returns> static bool splitp7c() { string listFile = YOUR_PKID + ".p7c"; string certFile; int nCerts; int iCert; int n; // How many certificates? nCerts = X509.GetCertFromP7Chain("", listFile, 0); Console.WriteLine("X509.GetCertFromP7Chain(0) returns " + nCerts); // Enumerate through them all if (nCerts > 0) { for (iCert = 1; iCert <= nCerts; iCert++) { // NB 1..n not 0..n-1 certFile = "TheCert" + iCert + ".cer"; n = X509.GetCertFromP7Chain(certFile, listFile, iCert); if (n < 0) display_error(n); Console.WriteLine("Cert(" + iCert + ")->" + certFile); } } // But we don't know which one is ours...so see the next procedure // (it's most likely that the first one is yours, but we'll check anyway) return (nCerts > 0); } /// <summary> /// Find and extract our own certificate in the .p7c file /// </summary> /// <returns></returns> static bool findourcert() { // Your new certificate file to be copied string newCert = YOUR_PKID + ".cer"; // Input file string listFile = YOUR_PKID + ".p7c"; // Your own private key and password // Did we mention? DO NOT HARDCODE PRODUCTION PASSWORDS! string priKeyFile = YOUR_PKID + "_pri.p8"; string password = "password"; int nCerts, iCert; string certFile; int n, nLen; StringBuilder sbPriKey, sbPubKey; string matchingCert; // 1. Extract X.509 certificates from P7 cert list... // How many certificates? nCerts = X509.GetCertFromP7Chain("", listFile, 0); Console.WriteLine("X509.GetCertFromP7Chain(0) returns " + nCerts); // Enumerate through them all if (nCerts > 0) { for (iCert = 1; iCert <= nCerts; iCert++) { // NB 1..n not 0..n-1 certFile = "TheCert" + iCert + ".cer"; n = X509.GetCertFromP7Chain(certFile, listFile, iCert); if (n < 0) display_error(n); Console.WriteLine("Cert(" + iCert + ")->" + certFile); } } else { Console.WriteLine("ERROR: no certificate extracted"); return false; } // 2. Read in private key from encrypted file... sbPriKey = Rsa.ReadEncPrivateKey(priKeyFile, password); if (sbPriKey.Length == 0) { display_error(General.ErrorCode()); return false; } // Display some details about our private key Console.WriteLine("Private key is " + Rsa.KeyBits(sbPriKey) + " bits"); Console.WriteLine("Private key HashCode = 0x{0:X8}", Rsa.KeyHashCode(sbPriKey)); // 3. Test each certificate in the list against our private key... nLen = Rsa.KeyBytes(sbPriKey); matchingCert = ""; for (iCert = 1; iCert <= nCerts; iCert++) { certFile = "TheCert" + iCert + ".cer"; Console.WriteLine("For certificate " + certFile + "..."); // Read in the public key from the certificate sbPubKey = Rsa.GetPublicKeyFromCert(certFile); if (sbPubKey.Length == 0) { Console.WriteLine("ERROR: cannot read public key from cert '" + certFile + "'"); return false; } Console.WriteLine(" This public key is " + Rsa.KeyBits(sbPubKey) + " bits long"); Console.WriteLine(" Public key HashCode = 0x{0:X8}", Rsa.KeyHashCode(sbPubKey)); // Does this match our own private key? n = Rsa.KeyMatch(sbPriKey, sbPubKey); if (0 == n) { Console.WriteLine(" FOUND MATCH: private key in '" + priKeyFile + "' matches public key in '" + certFile + "'"); // We are done, so we could break the loop here, but we don't // to demonstrate what happens with the other certs that don't match matchingCert = certFile; } else { Console.WriteLine(" Private and public keys do not match."); } } Console.WriteLine("Compared all public keys."); // 4. Copy our cert if we found it if (matchingCert.Length > 0) { Console.WriteLine("Found a match for '" + matchingCert + "'"); // Copy the file we want, overwriting any existing file File.Copy(matchingCert, newCert, true); Console.WriteLine("Copied matching cert to '" + newCert + "'"); } else { Console.WriteLine("Did not find a match for the private key."); } // Clean up Wipe.String(sbPriKey); return (matchingCert.Length > 0); } /// <summary> /// Validate the certs in the p7c chain /// </summary> /// <returns></returns> static bool validatep7c() { // Input p7c chain file string p7cFile = YOUR_PKID + ".p7c"; // The trusted self-signed CA cert and its known SHA-1 thumbprint // -- YOU WILL NEED TO CHANGE THESE string trustedCert = "CA_Cert.cer"; string trustedThumb = "3867c2c9072362fa2565365b1e82b639a42f8220"; string thumb; int n; // 1. Does the trusted cert match its known thumbprint? thumb = X509.CertThumb(trustedCert, HashAlgorithm.Sha1); if (String.Compare(thumb, trustedThumb, true) == 0) { Console.WriteLine("OK, trusted certifcate has expected thumbprint."); } else { Console.WriteLine("ERROR: thumbprint of trusted cert does not match"); return false; } // 2. Use ValidatePath to check that the certificate path is valid // with the trusted cert as its ultimate parent n = X509.ValidatePath(p7cFile, trustedCert, false); if (n == 0) Console.WriteLine("OK, certification path is valid"); else if (n == 1) Console.WriteLine("ERROR: certification path is invalid"); else display_error(n); return (0 == n); } } }