I'm using the ncrypt.dll in order to create a hash using both MD5 + SHA1 for DTLS like this:
private static class NCryptInterop
{
private struct BCRYPT_PKCS1_PADDING_INFO
{
internal IntPtr pszAlgId;
}
[Flags]
private enum NCryptSignFlags
{
BCRYPT_PAD_PKCS1 = 2,
}
[DllImport("ncrypt.dll")]
private static extern int NCryptSignHash(
SafeNCryptKeyHandle hKey,
ref BCRYPT_PKCS1_PADDING_INFO padding,
ref byte pbHashValue,
int cbHashValue,
ref byte pbSignature,
int cbSignature,
out int cbResult,
NCryptSignFlags dwFlags);
internal static byte[] SignHashRaw(CngKey key, byte[] hash, int keySize)
{
int keySizeBytes = keySize / 8;
byte[] signature = new byte[keySizeBytes];
// The Handle property returns a new object each time.
using (SafeNCryptKeyHandle keyHandle = key.Handle)
{
// Leave pszAlgId NULL to "raw sign"
BCRYPT_PKCS1_PADDING_INFO paddingInfo = new BCRYPT_PKCS1_PADDING_INFO();
int result = NCryptSignHash(
keyHandle,
ref paddingInfo,
ref hash[0],
hash.Length,
ref signature[0],
signature.Length,
out int cbResult,
NCryptSignFlags.BCRYPT_PAD_PKCS1);
if (result != 0)
{
throw new CryptographicException(result);
}
if (cbResult != signature.Length)
{
throw new InvalidOperationException();
}
return signature;
}
}
}
which I got from the answer of my previous question years ago (Thank you bartonjs!). In order to do that, I need a CngKey
. I am currently getting the private key like this:
((RSACng)certificate.PrivateKey).Key;
which I know is now obsolete. I believe the new way is:
certificate.GetRSAPrivateKey();
but I have yet to find a way to convert from an RSA to a CngKey without exporting the certificate. Unfortunately, the certificate is not mine and I have no control over it, so I cannot just make it exportable and I do not have the password.
In .NET6 and 7 obtaining the private key the obsolete way works perfectly. However, in .NET8 something has changed and I get the exception Unable to cast object of type 'System.Security.Cryptography.RSABCrypt' to type 'System.Security.Cryptography.RSACng'.
Is there any way possible in .NET8 to get the private key as a CngKey without having to export? Or maybe ncrypt.dll can accept something other than a CngKey that I can get without having to export the private key?
I was able to finally figure this out. The exception I was getting was actually on getting the public key, not the private key so that simplified things a bit. This is how I was able to do it and it works for legacy .NET Standard 2.0 as well as .NET 6, 7, and 8
var privateRsa = cert.GetRSAPrivateKey() ?? throw new Exception("Unable to get RSA Private Key");
var privateRsaCng = privateRsa as RSACng ?? throw new Exception("Private Key is not an RSACng");
_PrivateKeyRsa = privateRsaCng.Key;
var publicRsa = cert.GetRSAPublicKey() ?? throw new Exception("Unable to get RSA Public Key");
var publicRsaCng = new RSACng();
publicRsaCng.ImportParameters(publicRsa.ExportParameters(false));
PublicKey = publicRsaCng.Key;