.NET - 将 RSACryptoServiceProvider 移植到 CNG 以签署数据
.NET - Porting RSACryptoServiceProvider to CNG for signing data
我正在尝试移植一个用 C# 编写的 RSA 签名函数,来自(相对容易使用)RSACryptoServiceProvider.SignHash method, to the CNG API, in order to use a specific cryptographic service provider。
这是原始(移植前)函数:
private static byte[] SignDigest(RSAParameters keyMaterial, byte[] digest, string hashAlgo)
{
using (var cryptoProvider = new RSACryptoServiceProvider())
{
cryptoProvider.ImportParameters( keyMaterial );
// hashAlgo can only be one of "SHA1", "SHA256", "SHA384" and "SHA512".
return cryptoProvider.SignHash( digest, CryptoConfig.MapNameToOID( hashAlgo ) );
}
}
很简单,你不觉得吗?移植到 CNG 时,这是迄今为止我能做的最好的事情:
private static byte[] SignDigest(RSAParameters keyMaterial, byte[] digest, string hashAlgo)
{
var size = 6 * 4 + keyMaterial.Exponent.Length + keyMaterial.Modulus.Length + keyMaterial.P.Length + keyMaterial.Q.Length;
var keyBlob = new byte[size];
using (var writer = new BinaryWriter(new MemoryStream(keyBlob)))
{
// This is BCRYPT_RSAKEY_BLOB structure (https://msdn.microsoft.com/en-us/library/windows/desktop/aa375531(v=vs.85).aspx).
writer.Write(0x32415352); // BCRYPT_RSAPRIVATE_MAGIC
writer.Write(keyMaterial.Modulus.Length * 8); // BitLength
writer.Write(keyMaterial.Exponent.Length); // cbPublicExp
writer.Write(keyMaterial.Modulus.Length); // cbModulus
writer.Write(keyMaterial.P.Length); // cbPrime1
writer.Write(keyMaterial.Q.Length); // cbPrime2
writer.Write(keyMaterial.Exponent); // PublicExponent
writer.Write(keyMaterial.Modulus); // Modulus
writer.Write(keyMaterial.P); // Prime1
writer.Write(keyMaterial.Q); // Prime2
}
// Function NCryptImportKey uses "RSAPRIVATEBLOB" to indicate a BCRYPT_RSAPRIVATE_BLOB structure.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa376276(v=vs.85).aspx.
var key = CngKey.Import( keyBlob, new CngKeyBlobFormat( "RSAPRIVATEBLOB" ), _myProvider );
IntPtr pPaddingInfo = question; // What do I specify here?
byte[] pbHashValue = question; // Should I calculate hash from digest? How can I make sure it will be valid for verification?
byte[] pbSignature = question; // Is the signature size related to hash size? Or RSA key size?
int dwFlags = question; // Should I use BCRYPT_PAD_PKCS1 here or simply zero?
int pcbResult;
int result;
using ( var hKey = key.Handle )
{
result = NCryptSignHash( hKey, pPaddingInfo, pbHashValue, pbHashValue.Length, pbSignature, pbSignature.Length, out pcbResult, dwFlags );
}
if (result != 0)
throw new Exception();
return TrimArray(pbSignature, pcbResult);
}
// As described on https://msdn.microsoft.com/en-us/library/windows/desktop/aa376295(v=vs.85).aspx.
[DllImport("ncrypt.dll")]
internal static extern NCryptErrorCode NCryptSignHash(
SafeNCryptKeyHandle hKey,
IntPtr pPaddingInfo,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue,
int cbHashValue,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature,
int cbSignature,
out int pcbResult,
int dwFlags);
我可以成功将密钥导入CNG,但我不知道如何进行签名操作。欢迎任何帮助...
如果您使用的是 .NET 4.6 或更高版本,那么在 var key = CngKey.Import(...)
之后
using (RSACng rsa = new RSACng(key))
{
// Consider changing your signature to take a HashAlgorithmName instead of string.
// I'm assuming you want RSA-SSA PKCS#1 v1.5 instead of RSA-SSA-PSS.
return rsa.SignHash(digest, new HashAlgorithmName(hashAlgo), RSASignaturePadding.Pkcs1);
}
或者,如果您想查看 P/Invokes,您可以查看 https://referencesource.microsoft.com/#System.Core/System/Security/Cryptography/RsaCng.cs,672daeef0962f4ad(假设您没有做任何违反 referencesource.microsoft.com 许可的事情)。
我正在尝试移植一个用 C# 编写的 RSA 签名函数,来自(相对容易使用)RSACryptoServiceProvider.SignHash method, to the CNG API, in order to use a specific cryptographic service provider。
这是原始(移植前)函数:
private static byte[] SignDigest(RSAParameters keyMaterial, byte[] digest, string hashAlgo)
{
using (var cryptoProvider = new RSACryptoServiceProvider())
{
cryptoProvider.ImportParameters( keyMaterial );
// hashAlgo can only be one of "SHA1", "SHA256", "SHA384" and "SHA512".
return cryptoProvider.SignHash( digest, CryptoConfig.MapNameToOID( hashAlgo ) );
}
}
很简单,你不觉得吗?移植到 CNG 时,这是迄今为止我能做的最好的事情:
private static byte[] SignDigest(RSAParameters keyMaterial, byte[] digest, string hashAlgo)
{
var size = 6 * 4 + keyMaterial.Exponent.Length + keyMaterial.Modulus.Length + keyMaterial.P.Length + keyMaterial.Q.Length;
var keyBlob = new byte[size];
using (var writer = new BinaryWriter(new MemoryStream(keyBlob)))
{
// This is BCRYPT_RSAKEY_BLOB structure (https://msdn.microsoft.com/en-us/library/windows/desktop/aa375531(v=vs.85).aspx).
writer.Write(0x32415352); // BCRYPT_RSAPRIVATE_MAGIC
writer.Write(keyMaterial.Modulus.Length * 8); // BitLength
writer.Write(keyMaterial.Exponent.Length); // cbPublicExp
writer.Write(keyMaterial.Modulus.Length); // cbModulus
writer.Write(keyMaterial.P.Length); // cbPrime1
writer.Write(keyMaterial.Q.Length); // cbPrime2
writer.Write(keyMaterial.Exponent); // PublicExponent
writer.Write(keyMaterial.Modulus); // Modulus
writer.Write(keyMaterial.P); // Prime1
writer.Write(keyMaterial.Q); // Prime2
}
// Function NCryptImportKey uses "RSAPRIVATEBLOB" to indicate a BCRYPT_RSAPRIVATE_BLOB structure.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa376276(v=vs.85).aspx.
var key = CngKey.Import( keyBlob, new CngKeyBlobFormat( "RSAPRIVATEBLOB" ), _myProvider );
IntPtr pPaddingInfo = question; // What do I specify here?
byte[] pbHashValue = question; // Should I calculate hash from digest? How can I make sure it will be valid for verification?
byte[] pbSignature = question; // Is the signature size related to hash size? Or RSA key size?
int dwFlags = question; // Should I use BCRYPT_PAD_PKCS1 here or simply zero?
int pcbResult;
int result;
using ( var hKey = key.Handle )
{
result = NCryptSignHash( hKey, pPaddingInfo, pbHashValue, pbHashValue.Length, pbSignature, pbSignature.Length, out pcbResult, dwFlags );
}
if (result != 0)
throw new Exception();
return TrimArray(pbSignature, pcbResult);
}
// As described on https://msdn.microsoft.com/en-us/library/windows/desktop/aa376295(v=vs.85).aspx.
[DllImport("ncrypt.dll")]
internal static extern NCryptErrorCode NCryptSignHash(
SafeNCryptKeyHandle hKey,
IntPtr pPaddingInfo,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue,
int cbHashValue,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature,
int cbSignature,
out int pcbResult,
int dwFlags);
我可以成功将密钥导入CNG,但我不知道如何进行签名操作。欢迎任何帮助...
如果您使用的是 .NET 4.6 或更高版本,那么在 var key = CngKey.Import(...)
using (RSACng rsa = new RSACng(key))
{
// Consider changing your signature to take a HashAlgorithmName instead of string.
// I'm assuming you want RSA-SSA PKCS#1 v1.5 instead of RSA-SSA-PSS.
return rsa.SignHash(digest, new HashAlgorithmName(hashAlgo), RSASignaturePadding.Pkcs1);
}
或者,如果您想查看 P/Invokes,您可以查看 https://referencesource.microsoft.com/#System.Core/System/Security/Cryptography/RsaCng.cs,672daeef0962f4ad(假设您没有做任何违反 referencesource.microsoft.com 许可的事情)。