/* $Id: PopulateKeyInfo.cs $
* Last updated:
* $Date: 2022-02-13 10:23:00 $
* $Version: 1.0.0 $
*/
/******************************* LICENSE ***********************************
* Copyright (C) 2022 David Ireland, DI Management Services Pty Limited.
* All rights reserved. <https://di-mgt.com.au> <https://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>
****************************************************************************
*/
using System;
using System.Text;
using System.IO;
using System.Diagnostics;
/*
* Requires DI Management CryptoSys Library available from <https://cryptosys.net/>.
* 1. CryptoSys PKI Pro: https://cryptosys.net/pki/
* EITHER add reference to
* `diCrSysPKINet.dll` (installed by default in `C:\Program Files (x86)\CryptoSysPKI\DotNet`)
* OR add the C# source code file `CryptoSysPKI.cs`
* directly to your project.
*/
using Pki = CryptoSysPKI;
namespace DIManagement.PopulateKeyInfo
{
class PopulateKeyInfo
{
// INLINE TEMPLATES...
// Edit these to suit requirements, e.g. remove "ds:" prefix, change white-space, remove unwanted nodes, etc.
const string KeyInfoTemplate = @"<ds:KeyInfo Id=""xmldsig-@!GUID!@-keyinfo"">
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>@!RSA-MOD!@</ds:Modulus>
<ds:Exponent>@!RSA-EXP!@</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
<ds:X509Data>
<ds:X509Certificate>@!X509-CERT!@</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
";
const string xadesSigningCertTemplate = @"<xades:SigningCertificate>
<xades:Cert>
<xades:CertDigest>
<ds:DigestMethod Algorithm=""@!CERT-DIG-ALG!@"" />
<ds:DigestValue>@!DIG-CERT!@</ds:DigestValue>
</xades:CertDigest>
<xades:IssuerSerial>
<ds:X509IssuerName>@!X509-ISSUERNAME!@</ds:X509IssuerName>
<ds:X509SerialNumber>@!X509-SERIALNUMBER!@</ds:X509SerialNumber>
</xades:IssuerSerial>
</xades:Cert>
</xades:SigningCertificate>
";
static void Main(string[] args)
{
string s;
s = MakeKeyInfo("AliceRSASignByCarl.cer");
Console.WriteLine(s);
s = MakeXadesSigningCert("AliceRSASignByCarl.cer");
Console.WriteLine(s);
s = MakeXadesSigningCert("AliceRSASignByCarl.cer", useSha256: true);
Console.WriteLine(s);
}
/// <summary>
/// Complete the KeyInfo template for given X.509 certificate
/// </summary>
/// <param name="certFileOrString">Filename of X.509 certificate or string containing base64 or PEM representation</param>
/// <returns>String containing completed XML element, or message starting with "**ERROR"</returns>
static string MakeKeyInfo(string certFileOrString)
{
string strMsg;
string s, xs;
string pubKey, certStr;
// Get template
s = KeyInfoTemplate;
// Read in certificate as a one-line base64 string
certStr = Pki.X509.ReadStringFromFile(certFileOrString);
if (certStr.Length == 0) {
strMsg = String.Format("**ERROR: Failed to read X.509 certificate in '{0}'", certFileOrString);
return strMsg;
}
// We will use the cert string from now on...
// Extract the RSA public key from the certificate
pubKey = Pki.Rsa.ReadPublicKey(certStr).ToString();
if (pubKey.Length == 0) {
strMsg = String.Format("**ERROR: Could not extract public key from certificate");
return strMsg;
}
// Compute and substitute values for placeholders
s = s.Replace("@!X509-CERT!@", certStr);
xs = Pki.Rng.Guid(); // NB different each time
s = s.Replace("@!GUID!@", xs);
// Compute and substitute RSAKeyValue components
xs = Pki.Rsa.KeyValue(pubKey, "Modulus");
s = s.Replace("@!RSA-MOD!@", xs);
xs = Pki.Rsa.KeyValue(pubKey, "Exponent");
s = s.Replace("@!RSA-EXP!@", xs);
return s;
}
/// <summary>
/// Complete the <c>xades:SigningCertificate</c> template for given X.509 certificate
/// </summary>
/// <param name="certFileOrString">Filename of X.509 certificate or string containing base64 or PEM representation</param>
/// <param name="useSha256">If true, use SHA-256 algorithm for CertDigest value (default = use SHA-1)</param>
/// <returns>String containing completed XML element, or message starting with "**ERROR"</returns>
static string MakeXadesSigningCert(string certFileOrString, bool useSha256 = false)
{
const string DIGALG = "http://www.w3.org/2000/09/xmldsig#sha1";
const string DIGALG256 = "http://www.w3.org/2001/04/xmlenc#sha256";
string strMsg;
string s, xs;
string certStr;
Pki.HashAlgorithm hashAlg = Pki.HashAlgorithm.Sha1;
// Get template
s = xadesSigningCertTemplate;
// Read in certificate as a one-line base64 string
certStr = Pki.X509.ReadStringFromFile(certFileOrString);
if (certStr.Length == 0) {
strMsg = String.Format("**ERROR: Failed to read X.509 certificate in '{0}'", certFileOrString);
return strMsg;
}
// Extract and substitute values for IssuerSerial
xs = Pki.X509.QueryCert(certStr, "serialNumber", Pki.X509.OutputOpts.Decimal);
s = s.Replace("@!X509-SERIALNUMBER!@", xs);
xs = Pki.X509.QueryCert(certStr, "issuerName", Pki.X509.OutputOpts.Ldap);
s = s.Replace("@!X509-ISSUERNAME!@", xs);
// Compute CertDigest value
if (useSha256) {
// Use SHA-256 algorithm
s = s.Replace("@!CERT-DIG-ALG!@", DIGALG256);
hashAlg = Pki.HashAlgorithm.Sha256;
} else {
// Default SHA-1
s = s.Replace("@!CERT-DIG-ALG!@", DIGALG);
}
xs = Pki.Cnv.ToBase64(Pki.Cnv.FromHex(Pki.X509.CertThumb(certStr, hashAlg)));
s = s.Replace("@!DIG-CERT!@", xs);
return s;
}
}
}