using System;
using System.Text;
using System.Diagnostics;
using CryptoSysPKI;
/* Demonstrates a one-off processing of a PFX file.
* 1. Show we can read in private key just like we can from a PKCS#8 key file.
* 2. Extract our X.509 certificate.
* 3. Extract all certificates in the chain.
* 4. Show details of all these certificates to use in XML-DSIG.
* 5. Compose a <xades:Cert> element for a XADES file
*/
/******************************* LICENSE ***********************************
* Copyright (C) 2019 David Ireland, DI Management Services Pty Limited.
* All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
* The code in this module is licensed under the terms of the MIT license.
* For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/
namespace ExtractFromPFX
{
class ExtractFromPFX
{
static void ProcessPFX()
{
string pfxfile, pwd;
string certfile, p7file;
StringBuilder sbPriKey, sbPubKey;
int r, nCerts, i;
string fname, s, query;
string template, xmlelement, hashalg;
// Our PFX file (could also be .p12 = same thing)
pfxfile = "enid.pfx";
pwd = "password"; // Danger, Will Robinson!
// 1. First show we can read in our private key directly from the PFX file
// (just like from a PKCS#8 key file)
// We can use this "internal" private key string to, e.g., sign data.
sbPriKey = Rsa.ReadPrivateKey(pfxfile, pwd);
Console.WriteLine("Rsa.ReadPrivateKey() returns an internal string of length {0}", sbPriKey.Length);
Debug.Assert(sbPriKey.Length > 0, "Failed to read private key from PFX");
Console.WriteLine("Pri key bits = {0}", Rsa.KeyBits(sbPriKey.ToString()));
// 2. Extract our X.509 certificate
certfile = "mycert.cer";
r = X509.GetCertFromPFX(certfile, pfxfile, pwd);
Console.WriteLine("X509.GetCertFromPFX() returns {0} (expected +ve)", r);
// 2a. Read in our public key from this certificate
sbPubKey = Rsa.ReadPublicKey(certfile);
Debug.Assert(sbPubKey.Length > 0, "Failed to read public key from certificate");
Console.WriteLine("Pub key bits = {0}", Rsa.KeyBits(sbPubKey.ToString()));
// 2b. Show these two keys are matched
Console.WriteLine("Pri key hashcode = {0:X8}", Rsa.KeyHashCode(sbPriKey));
Console.WriteLine("Pub key hashcode = {0:X8}", Rsa.KeyHashCode(sbPubKey));
// 2c. alternatively...
r = Rsa.KeyMatch(sbPriKey.ToString(), sbPubKey.ToString());
Console.WriteLine("Rsa.KeyMatch() returns {0} (expected 0)", r);
Debug.Assert(0 == r, "Keys do not match");
// 3. Extract all certificates in the chain as a single P7 file
// (strictly should be .p7c but .p7b works better in Windows)
p7file = "certs.p7b";
r = X509.GetP7ChainFromPFX(p7file, pfxfile, pwd);
Console.WriteLine("X509.GetP7ChainFromPFX() returns {0} (expected +ve)", r);
Debug.Assert(r > 0, "Failed to extract P7 file from PFX");
// 3a. Validate the chain
r = X509.ValidatePath(p7file);
Console.WriteLine("X509.ValidatePath() returns {0} (expected 0)", r);
Debug.Assert(r == 0, "Path validation failed");
// 4. Extract certs individually from the P7 file
// and show details of these certificates.
// How many certs are there?
nCerts = (int)X509.GetCertFromP7Chain("", p7file, 0);
Console.WriteLine("nCerts = {0}", nCerts);
Debug.Assert(nCerts > 0, "No certs found in P7 file");
// Enumerate through the certs
for (i = 1; i <= nCerts; i++) {
fname = "cert" + i + ".cer";
Console.WriteLine("FILE: {0}", fname);
r = X509.GetCertFromP7Chain(fname, p7file, i);
Debug.Assert(r > 0);
// 4a. Extract details from certificate in XML-DSIG-required form (LDAP, decimal)
query = "subjectName";
s = X509.QueryCert(fname, query, X509.OutputOpts.Ldap);
Console.WriteLine("{0}: {1}", query, s);
query = "issuerName";
s = X509.QueryCert(fname, query, X509.OutputOpts.Ldap);
Console.WriteLine("{0}: {1}", query, s);
query = "serialNumber";
s = X509.QueryCert(fname, query, X509.OutputOpts.Decimal);
Console.WriteLine("{0}: {1}", query, s);
// And compute its SHA-1 digest value ("thumbprint")
s = X509.CertThumb(fname, HashAlgorithm.Sha1);
Console.WriteLine("DigestValue (hex) = {0}", s);
// We want the base64-encoded form for XML DigestValue elements
Console.WriteLine("DigestValue (b64) = {0}", Cnv.ToBase64(Cnv.FromHex(s)));
}
// 5. Compose a <xades:Cert> element for a XADES file
Console.WriteLine("FILE: {0}", certfile);
template = @"<xades:Cert>
<xades:CertDigest>
<ds:DigestMethod Algorithm=""@!HASH-ALG!@"" />
<ds:DigestValue>@!DIGEST-VALUE!@</ds:DigestValue>
</xades:CertDigest>
<xades:IssuerSerial>
<ds:X509IssuerName>@!ISSUER-NAME!@</ds:X509IssuerName>
<ds:X509SerialNumber>@!SERIAL-NUMBER!@</ds:X509SerialNumber>
</xades:IssuerSerial>
</xades:Cert>";
// Get element content in required form (LDAP, decimal) from cert then substitute in template.
xmlelement = template;
// 5a. Use SHA-1
hashalg = "http://www.w3.org/2000/09/xmldsig#sha1";
xmlelement = xmlelement.Replace("@!HASH-ALG!@", hashalg);
s = X509.CertThumb(certfile, HashAlgorithm.Sha1);
s = Cnv.ToBase64(Cnv.FromHex(s));
xmlelement = xmlelement.Replace("@!DIGEST-VALUE!@", s);
s = X509.QueryCert(certfile, "issuerName", X509.OutputOpts.Ldap);
xmlelement = xmlelement.Replace("@!ISSUER-NAME!@", s);
s = X509.QueryCert(certfile, "serialNumber", X509.OutputOpts.Decimal);
xmlelement = xmlelement.Replace("@!SERIAL-NUMBER!@", s);
Console.WriteLine(xmlelement);
// 5b. Repeat using SHA-256
xmlelement = template;
hashalg = "http://www.w3.org/2001/04/xmlenc#sha256"; // CHANGED
xmlelement = xmlelement.Replace("@!HASH-ALG!@", hashalg);
s = X509.CertThumb(certfile, HashAlgorithm.Sha256); // CHANGED
s = Cnv.ToBase64(Cnv.FromHex(s));
xmlelement = xmlelement.Replace("@!DIGEST-VALUE!@", s);
s = X509.QueryCert(certfile, "issuerName", X509.OutputOpts.Ldap);
xmlelement = xmlelement.Replace("@!ISSUER-NAME!@", s);
s = X509.QueryCert(certfile, "serialNumber", X509.OutputOpts.Decimal);
xmlelement = xmlelement.Replace("@!SERIAL-NUMBER!@", s);
Console.WriteLine(xmlelement);
}
static void Main(string[] args)
{
ProcessPFX();
}
}
}