C#

时间:2019-10-11 13:56:17

标签: c# rsa md5 digital-signature sha1

我需要使用RSA signMD5散列的组合来SHA1进行一些数据处理。我可以轻松地仅对其中一个进行此操作,但是将其组合起来会很棘手。这是给certificate verify中的DTLS 1.0消息的消息,因此,不幸的是,我不能选择只做一个。

我相信要在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进行签名吗?

1 个答案:

答案 0 :(得分:1)

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

鉴于您已经具有RSACryptoServiceProvider格式的持久密钥(名为rsaCsp)和要签名的数据(名为toBeSigned),可以

  • 重新打开CNG中的CAPI密钥(几乎总是有效)
  • 请CNG为您签名
    • 通过不指定哈希算法名称,它假定您已完成所需的任何数据框架。就您而言,只需串联数据,而不将其放入DigestInfo中即可。

听起来有些棘手,但实际上并非如此。

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

如果Interop可以工作,那需要花点时间:

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类型确实为您节省了很多工作。