在 C# 中使用 MD5+SHA1 哈希的 RSA 签名



我需要使用MD5SHA1哈希的组合来RSA sign一些数据。我可以轻松地只为一个或另一个做到这一点,但组合会让它变得棘手。这是为了DTLS 1.0中的certificate verify消息,所以不幸的是,我不能选择只做一个。

我相信步骤是在MD5中散列,然后在SHA1中散列并连接。然后我需要用私钥加密。这就是我被困住的地方。

我有一个具有正确私钥的RSACryptoServiceProvider(不可导出(。我知道我做不到

rsa.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);

因为它不仅仅是 SHA1 哈希,所以此函数将哈希视为无效,因为它是 36 字节而不是预期的 20 字节。

我也知道我做不到

rsa.SignData(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);

因为这又要散列了。

我相信我更接近

rsa.Encrypt(hash, RSAEncryptionPadding.Pkcs1);

但是,这是使用公钥而不是我需要的私钥。

关于如何使用私钥加密或使用 MD5+SHA1 进行签名的任何建议?

您无法在托管层中执行此操作,但可以让本机层为您执行此操作。

假设您已经有一个 RSACryptoServiceProvider 格式的持久密钥(名为rsaCsp(和要签名的数据(名为toBeSigned(,您可以

  • 在 CNG 中重新打开 CAPI 密钥(几乎总是有效(
  • 让 CNG 为您签名
    • 通过不指定哈希算法名称,它假定您已完成所需的任何数据成帧。 在您的情况下,只需连接数据,而不是将其构建在摘要信息中。

听起来很棘手,但事实并非如此。

byte[] md5;
byte[] sha1;
using (HashAlgorithm hash = MD5.Create())
{
md5 = hash.ComputeHash(toBeSigned);
}
using (HashAlgorithm hash = SHA1.Create())
{
sha1 = hash.ComputeHash(toBeSigned);
}
byte[] all = md5.Concat(sha1).ToArray();
CspKeyContainerInfo cspInfo = rsaCsp.CspKeyContainerInfo;
CngProvider provider = new CngProvider(cspInfo.ProviderName);
CngKeyOpenOptions options = CngKeyOpenOptions.None;
if (cspInfo.MachineKeyStore)
{
options = CngKeyOpenOptions.MachineKey;
}
using (CngKey cngKey = CngKey.Open(cspInfo.KeyContainerName, provider, options))
{
return NCryptInterop.SignHashRaw(cngKey, all, rsaCsp.KeySize);
}

如果互操作有效,这需要您做一点点:

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;
}
}
}

从理论上讲,也可以使用 CAPI 执行此操作(如果您的私钥恰好位于没有 CNG 驱动程序的 HSM 中(...但是你必须玩更多的指针。 CngKey 类型确实为您节省了大量工作。

最新更新