Search code examples
c#cryptographyasp.net-core-2.0cng

How to get CngKey without using GetCngPrivateKey in net core 2.0


I'm reading a certificate from a X509Store, that I want to get it's CngKey using GetCngPrivateKey. But all I'm getting is the following error.

at Security.Cryptography.X509Certificates.X509Native.AcquireCngPrivateKey(SafeCertContextHandle certificateContext)\r\n at Security.Cryptography.X509Certificates.X509Certificate2ExtensionMethods.GetCngPrivateKey(X509Certificate2 certificate)\r\n

The certificate is passing the validation HasCngKey, but I'm not able to get its value. Is there any other way to get the CngKey?

byte[] digest = null;
using (var hmac = SHA256.Create())
{
digest = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data));
}
var certificate = collection[0];

byte[] firmadigital = null;
CngKey privateKey = null;
AsymmetricAlgorithm privateKey1 = null;

var hasCng = certificate.HasCngKey();
if (hasCng)
{
   privateKey = certificate.GetCngPrivateKey();
   Console.WriteLine("tiene CNG");
   var key = new Security.Cryptography.RSACng(privateKey);
   key.SignatureHashAlgorithm = CngAlgorithm.Sha1;
   var p = key.SignData(digest);
   return Convert.ToBase64String(p);
}

I'm using c# net core 2.0

Thanks


Solution

  • You seem to be using an extra library, since neither .NET Framework nor .NET Core has HasCngKey, GetCngPrivateKey(), or a property-based RSACng class. I'm guessing you're using the cryptography library from the clrsecurity CodePlex prototype.

    The functionality you're using is in .NET Framework (as of 4.6) and .NET Core (all versions), but under different names.

    Instead of

    var hasCng = certificate.HasCngKey();
    if (hasCng)
    {
       privateKey = certificate.GetCngPrivateKey();
       Console.WriteLine("tiene CNG");
       var key = new Security.Cryptography.RSACng(privateKey);
       key.SignatureHashAlgorithm = CngAlgorithm.Sha1;
       var p = key.SignData(digest);
       return Convert.ToBase64String(p);
    }
    

    you want

    using (RSA rsa = certificate.GetRSAPrivateKey())
    {
        Console.WriteLine("tiene RSA. Que tipo no es importante.");
        var p = rsa.SignData(digest, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
        return Convert.ToBase64String(p);
    }
    

    It is correct to put the object from GetRSAPrivateKey() into a using block, because the method returns a distinct object every call.

    The reason that GetRSAPrivateKey() was chosen (along with fixing up the RSA base class to be more useful) was so the same code works for Windows CNG, Windows CAPI, Linux, and macOS. The only reason to ever inspect the returned object and cast further is to do pointer-based interop with the OS/cryptolib. (But since Windows can return CNG or CAPI as necessary, it's tough to get even that right)