// $Id: GermanHealthSetup2019.cs $
// You should only need to do these procedures once. Updated for 2018-19 changes.
// Last Updated:
// $Date: 2019-06-26 08:11:00 $
/********************************************************************************
* Copyright (c) 2014-19 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.
* The code in this module is licensed under the terms of the MIT license.
* For a copy, see <http://opensource.org/licenses/MIT>
********************************************************************************
*/
/* Changes from 2014 version:
* --------------------------
* New RSA keys are generated with 4096 bits, up from 2048.
* Uses stronger encryption (AES-128/SHA-256) when encrypted private key is created.
* Uses RSA-PSS-SHA256 to sign PKSC#10 certificate request.
* Updated deprecated options and methods, e.g. Cms.Options.
*/
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 < 120200)
{
Console.WriteLine("Require CryptoSys PKI version 12.2.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 4096-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, 4096, Rsa.PublicExponent.Exp_EQ_65537, 5000, password, CipherAlgorithm.Aes128, HashAlgorithm.Sha256, Rsa.Format.Binary, 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";
// CHANGED [2019-06-26] - use RSA-PSS instead of RSA-SHA256
// Create certificate request, signing with Rsa_Pss_Sha256
int n = X509.CertRequest(reqFile, priKeyFile, distName, "", password, SigAlgorithm.Rsa_Pss_Sha256, X509.CsrOptions.FormatBinary);
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.
/// 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.SigAlg.Default, Cms.SigDataOptions.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;
// How many certificates?
int nCerts = X509.GetCertCountInP7Chain(listFile);
Console.WriteLine("X509.GetCertCountInP7Chain() returns " + nCerts);
// Enumerate through them all
if (nCerts > 0)
{
for (int iCert = 1; iCert <= nCerts; iCert++)
{ // NB 1..n not 0..n-1
certFile = "TheCert" + iCert + ".cer";
int 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.GetCertCountInP7Chain(listFile);
Console.WriteLine("X509.GetCertCountInP7Chain() 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.ReadPrivateKey(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.ReadPublicKey(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);
}
}
}