【问题标题】:RSA Signature with MD5+SHA1 hash in C#C# 中带有 MD5+SHA1 哈希的 RSA 签名
【发布时间】:2019-10-11 13:56:17
【问题描述】:

我需要使用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 进行签名的任何建议?

【问题讨论】:

  • 似乎不受支持。您必须使用BigInteger 编写您自己的代码。听起来很可怕。祝你好运。
  • 所以我实际上看到了另一个问题,但问题是私钥不可导出,所以我无法访问指数或模数stackoverflow.com/questions/19407581/…
  • 微软的示例代码真的很糟糕。您需要在加密和解密时添加相同的私钥,并仅示例如何生成随机密钥。请参阅以下内容:docs.microsoft.com/en-us/dotnet/standard/security/…
  • 所以创建一个可导出的新私钥。如果您关闭所有解决问题的途径,那么您将无法解决问题。
  • 这不是我的钥匙,我无法控制。没有别的办法了吗?

标签: c# rsa md5 digital-signature sha1


【解决方案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 类型在这里确实为您节省了很多工作。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2014-02-07
  • 2011-08-16
  • 1970-01-01
  • 1970-01-01
  • 2012-04-15
  • 2010-12-08
  • 1970-01-01
  • 2013-06-17
相关资源
最近更新 更多