I have created a selfsign certificate using OpenSSL as follows:
openssl req -x509 -sha512 -new keyrsa:2048 -keyout key2.pem -out cert2.pem -days 100
openssl pkcs12 -export -out pkcs12_cert_test2.pfx -inkey key2.pem -in cert2.pem
I have installed pkcs12_cert_test2.pfx
on Windows and the certificate sign algorithm value is sha512RSA
Then, I have coded the following in C# .NET 4.0:
public static bool DSHandler(string operation, string path, string devicePath)
{
bool result = false;
CryptoConfig.AddAlgorithm(typeof(USBSaferAppEFB.RsaPkCs1Sha512SignatureDescription),
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
string password = "xxxx";
bool validSignature = false;
byte[] hashchain = generateHash(path);
string digSignFile = Utils.RegVarValue(DIGSIGNFILE);
string subjectDN = Utils.RegVarValue(SUBJECTDN);
if (operation == "sign")
{
byte[] signature = SignFromContainer(hashchain, subjectDN);
if (signature != null)
{
string signaturestring = Convert.ToBase64String(signature);
File.WriteAllBytes(devicePath + digSignFile, signature);
result = true;
}
else
result = false;
}
}
static byte[] SignFromContainer(byte[] hashchain, string certSubject)
{
try
{
X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
my.Open(OpenFlags.ReadOnly);
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// We found it.
// Get its associated CSP and private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
return null;
}
byte[] myHash = { 59,4,248,102,77,97,142,201,
210,12,224,93,25,41,100,197,
210,12,224,93};
// Sign the hash
return csp.SignHash(hashchain, CryptoConfig.MapNameToOID("SHA-512"));
}
catch (Exception e)
{
log.Logger("Exception: "+e.Message, "debug");
log.Logger(e.StackTrace, "debug");
return null;
}
}
Using solutions from some articles and answers, I have RsaPkCs1Sha512SignatureDescription
class where I try to implement and register the signature description:
public class RsaPkCs1Sha512SignatureDescription : SignatureDescription
{
public RsaPkCs1Sha512SignatureDescription()
{
KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
DigestAlgorithm = typeof(SHA512Managed).FullName;
FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
{
var sigProcessor = (AsymmetricSignatureDeformatter)CryptoConfig.CreateFromName(DeformatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA-512");
return sigProcessor;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
{
var sigProcessor =
(AsymmetricSignatureFormatter)CryptoConfig.CreateFromName(FormatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA-512");
return sigProcessor;
}
}
I have verified that hash is 64
bytes long and it returns a CryptographicException: Bad Hash
. But if I use myHash
var (20 bytes long), although algorithm specified in SignHash
is SHA512
, it works, and sign the hash using SHA1
algorithm.
Also, if I print csp.SignatureAlgorithm
, its value is http://www.w3.org/2000/09/xmldsig#rsa-sha1. Why is signature algorithm equals to http://www.w3.org/2000/09/xmldsig#rsa-sha1 if the detail of the installed certificate shows sha512RSA
as sign algorithm? Could be this point the real problem? If so, how could I create the certificate properly? Thanks in advance!
This process has been simplified in .NET 4.6:
byte[] signature;
using (RSA rsa = cert.GetRSAPrivateKey())
{
signature = rsa.SignHash(hash, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
}
Note that the .NET 4.6 RSA type no longer requires casting to RSACryptoServiceProvider, and if you use GetRSAPrivateKey() (instead of the PrivateKey property) the returned object is likely to handle SHA-512 signature generation. GetRSAPrivateKey also returns a unique object each time, so you should Dispose it appropriately.
Ok. It is not necessary to implement RsaPkCs1Sha512SignatureDescription
class or something similiar but to specify the right CSP to allow CryptoAPI to use SHA256 or SHA512: "Microsoft Enhanced RSA and AES Cryptographic Provider", type 24.
We only need to put CSP type as CspParameter and export/import key info:
byte[] privateKeyBlob;
CspParameters cp = new CspParameters(24);
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PrivateKey;
privateKeyBlob = csp.ExportCspBlob(true);
csp = new RSACryptoServiceProvider(cp);
csp.ImportCspBlob(privateKeyBlob);
// Sign the hash
return csp.SignHash(hashchain, CryptoConfig.MapNameToOID("SHA-512"));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With