Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - Is it possible to Sign hash using RSA SHA512?

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!

like image 733
AdSsa Avatar asked May 27 '16 12:05

AdSsa


2 Answers

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.

like image 169
bartonjs Avatar answered Sep 29 '22 23:09

bartonjs


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"));
like image 29
AdSsa Avatar answered Sep 29 '22 23:09

AdSsa