// $Id: GermanHealthExamples.cs $ // Examples using CryptoSys PKI to create and read signed-and-enveloped PKCS7 (CMS) objects suitable for // the Security interface for data exchange in the health service version 3.0 (as far as we know). // Last Updated: // $Date: 2014-09-08 17:37:00 $ /******************************************************************************* ' 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 GermanHealthExamples { class GermanHealthExamples { 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; } // 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 "maketest": maketest(); break; case "readtest": readtest(); break; default: usage(); break; } } static void usage() { Console.WriteLine("ERROR: Expecting command: 'maketest',..."); System.Environment.Exit(1); } static void display_error(int code) { Console.WriteLine("ERROR: " + General.ErrorLookup(code) + ": " + General.LastError()); } /// <summary> /// Make a test signed-and-enveloped-data object /// </summary> /// <returns></returns> static bool maketest() { // The message we want to send string msg = "Hallo Walt"; // Final enveloped-data file to send to recipient string outputFile = "To_999009051a.p7m"; // Our private key data string priKeyFile = "999009991a_pri.p8"; StringBuilder sbPassword = new StringBuilder("password"); // Signer's certificate plus (optionally) the certs in the chain that signed it. // Separated by a semi-colon ";". // NOTE: the first cert in the list MUST be the signers // -- it's up to you to work out which is which (see Check_CertList_With_PrivateKey()) string signersCertList = "999009991a.cer" + ";" + "Int_Cert.cer" + ";" + "CA_Cert.cer"; // The certificate of the recipient -- this must be provided // (otherwise we wouldn't know whom to send it to) string recipCertFile = "999009051a.cer"; // Display input Console.WriteLine("Message to be signed and enveloped='" + msg + "'"); bool isok = Make_Signed_And_EnvelopedData(outputFile, msg, priKeyFile, sbPassword.ToString(), signersCertList, recipCertFile, true); if (isok) Console.WriteLine("OK, created output file '" + outputFile + "'"); else Console.WriteLine("FAILED!"); Wipe.String(sbPassword); return true; } /// <summary> /// Read a test signed-and-enveloped-data file /// </summary> /// <remarks> /// NOTE: we can only do this because we have the private key for the dummy user with id IK999009051, /// which, of course, you would not normally have. /// To test yourself, send a test message to yourself signed by yourself. /// </remarks> /// <returns></returns> static bool readtest() { string inputFile = "To_999009051a.p7m"; // Recipient's private key data string priKeyFile = "999009051a_pri.p8"; StringBuilder sbPassword = new StringBuilder("password"); // Display input Console.WriteLine("Reading input file '" + inputFile + "'"); string msg = Read_Signed_and_Enveloped_Data(inputFile, priKeyFile, sbPassword.ToString(), true); Console.WriteLine("Result='" + msg + "'"); // Clean up password Wipe.String(sbPassword); return true; } /*********************/ /* GENERIC FUNCTIONS */ /*********************/ /// <summary> /// Creates a PKCS#7 signed-and-enveloped-data object /// </summary> /// <param name="outputFile">File to be created</param> /// <param name="msg">Message to be sent</param> /// <param name="priKeyFile">Sender's encrypted key file</param> /// <param name="password">Password for key file</param> /// <param name="signersCertList">List of signers' certificates, sender's first</param> /// <param name="recipCertFile">Filename of recipient's certificate</param> /// <param name="keepInterFile">True to keep intermediate file</param> /// <returns>True if successful; otherwise false</returns> static bool Make_Signed_And_EnvelopedData(string outputFile, string msg, string priKeyFile, string password, string signersCertList, string recipCertFile, bool keepInterFile) { int n; // Intermediate signed-data file we will create string sigFile = outputFile + ".int.tmp"; // 1. Read in the secret private key to a StringBuilder Console.WriteLine("Reading private key from PRI file..."); StringBuilder sbPriKey = Rsa.ReadEncPrivateKey(priKeyFile, password); // Check for success if (sbPriKey.Length == 0) { Console.WriteLine("ERROR: Cannot read private key"); return false; } else { Console.WriteLine("...OK, read in private key: key length=" + Rsa.KeyBits(sbPriKey) + " bits"); } // 2. Create a signed-data object with signed attributes and signing-time and all certificates. // using SHA-256 for the signature n = Cms.MakeSigDataFromString(sigFile, msg, signersCertList, sbPriKey.ToString(), HashAlgorithm.Sha256, Cms.SigDataOptions.IncludeAttributes | Cms.SigDataOptions.AddSignTime); Console.WriteLine("CMS_MakeSigDataFromString returns " + n + " (expecting 0)"); // Clean up as we go Wipe.String(sbPriKey); // Check for success if (n != 0) { display_error(n); return false; } else { Console.WriteLine("OK, created signed-data file '" + sigFile + "'"); } // 3. Encrypt the signed-data object directly using the recipient's certificate // -- this produces a binary enveloped-data file // -- Use Triple DES (Tdea) for now. In 2016 (or thereabouts) change to Aes256 n = Cms.MakeEnvData(outputFile, sigFile, recipCertFile, CipherAlgorithm.Tdea, 0); // Clean up sensitive data if (!keepInterFile) { Wipe.File(sigFile); } // Check for success (NB expecting # of recipients, not zreo) if (n <= 0) { display_error(n); return false; } else { Console.WriteLine("OK, created enveloped-data file '" + outputFile + "'"); } // Now send the output file to the recipient... return true; } /// <summary> /// Read a signed-and-enveloped-data object using recipient's private key /// </summary> /// <param name="inputFile">signed-and-enveloped-data object file</param> /// <param name="priKeyFile">recipient's encrypted private key file</param> /// <param name="password">password for private key</param> /// <returns>String containing extracted message or empty string on error</returns> static string Read_Signed_and_Enveloped_Data(string inputFile, string priKeyFile, string password, bool keepInterFile) { string msg = ""; int n; string s, query; // 1. Read in the recipient's secret private key to a StringBuilder Console.WriteLine("Reading private key from PRI file..."); StringBuilder sbPriKey = Rsa.ReadEncPrivateKey(priKeyFile, password); // Check for success if (sbPriKey.Length == 0) { Console.WriteLine("ERROR: Cannot read private key"); return ""; } else { Console.WriteLine("...OK, read in private key: key length=" + Rsa.KeyBits(sbPriKey) + " bits"); } // Intermediate file we will create string sigFile = inputFile + ".i2.tmp"; // 2. Read the encrypted data from the enveloped-data file n = Cms.ReadEnvDataToFile(sigFile, inputFile, "", sbPriKey.ToString(), 0); Console.WriteLine("Cms.ReadEnvDataToFile returns " + n + " (expected 0)"); // Check for success if (n != 0) { display_error(n); return ""; } else { Console.WriteLine("Extracted signed-data file '" + sigFile + "'"); } // 2a. While we're here, get some info from the signed-data file Console.WriteLine("For signed-data file: '" + sigFile + "'"); s = Cms.QuerySigData(sigFile, "version"); Console.WriteLine(" Version=" + s); query = "signingTime"; s = Cms.QuerySigData(sigFile, query); Console.WriteLine(" " + query + "=" + s); query = "messageDigest"; s = Cms.QuerySigData(sigFile, query); Console.WriteLine(" " + query + "=" + s); query = "digestAlgorithm"; s = Cms.QuerySigData(sigFile, query); Console.WriteLine(" " + query + "=" + s); query = "countOfSignerInfos"; s = Cms.QuerySigData(sigFile, query); Console.WriteLine(" " + query + "=" + s); query = "CountOfCertificates"; s = Cms.QuerySigData(sigFile, query); Console.WriteLine(" " + query + "=" + s); // 2b. And verify the signed data n = Cms.VerifySigData(sigFile); Console.WriteLine("Cms.VerifySigData returns " + n + " (expected 0)"); // 3. Extract the content msg = Cms.ReadSigDataToString(sigFile); // Clean up sensitive data if (!keepInterFile) { Wipe.File(sigFile); } // Check for success if (msg.Length == 0) { display_error(General.ErrorCode()); return ""; } return msg; } } }