【问题标题】:Digital Signatures: Encrypting the Hash vs Signing the Hash?数字签名:加密哈希与签名哈希?
【发布时间】:2017-04-25 14:50:22
【问题描述】:

我正在尝试实现 SHA256-RSA 数字签名,但我对 C# 中的术语和实现感到困惑。

AFAIK,“签署文件”是生成文件的哈希,然后加密该哈希。我也听说过“签署哈希”这个短语。这是同一件事吗?或者这是散列一个散列然后加密散列'?

这是有问题的代码:

public void SignatureTest(byte[] data, X509Certificate2 cert)
{
    var sha256 = new SHA256CryptoServiceProvider();
    var rsa = (RSACryptoServiceProvider)cert.PrivateKey;

    var hashOfData = sha256.ComputeHash(data);

    var encryptedHash = rsa.Encrypt(hashOfData, false);
    var encryptedHashOAEP = rsa.Encrypt(hashOfData, true);            
    var signedHash = rsa.SignHash(hashOfData, "SHA256");

    //Shouldn't one of these be true?
    var false1 = CompareAsBase64Str(encryptedHash, signedHash);
    var false2 = CompareAsBase64Str(encryptedHashOAEP, signedHash);

    //This is the one that actually matches
    var true1 = CompareAsBase64Str(signedHash, rsa.SignData(data, sha256));
}

public bool CompareAsBase64Str(byte[] b1, byte[] b2)
{
    return (Convert.ToBase64String(b1) == Convert.ToBase64String(b2));
}

这是 MSDN 在 RSACryptoServiceProvider 上所说的:

SignHash() 计算指定哈希值的签名通过使用私钥加密。

Encrypt() 使用 RSA 算法加密数据。

SignHash(hash) 和 Encrypt(hash) 应该一样吗?

【问题讨论】:

    标签: c# encryption cryptography certificate


    【解决方案1】:

    您需要将关注点分开,这将有助于您理解术语。

    可以任意组合使用hashed 和/或encrypted 任意数据块。 Hash 意思是:使用密码算法生成一个不可逆(即仅仅通过知道算法和哈希就无法重构原始数据)且一致(即给定相同的数据和算法,其值产生的哈希值始终相同)。

    Encrypt 表示:使用加密算法用给定的密钥(密钥可以是对称的或非对称的)对数据(全部或成块)进行加密。

    Sign 表示:Hash 数据和Encrypt 具有给定键的哈希。然后,给定一对(对于非对称)或相同(对于对称)密钥,消费者可以验证:

    1. 哈希匹配,这意味着数据在传输过程中没有改变
    2. 哈希确实来自至少具有配对密钥(对于非对称)或相同密钥(对于对称)的源

    【讨论】:

    • 谢谢!我对here、here 和其他地方提到的“签名哈希”一词更加困惑。如果我弄错了,请纠正我:所以动词“签名”是“签署文件”与“签署哈希”的不同过程。对文件进行签名就是生成该文件的散列,然后使用标识散列的附加数据对该散列进行加密。对哈希进行签名就是用额外的识别数据加密哈希?
    • @nr-91 Sign 是完全相同的过程,无论您签署什么。当您传输非常大的数据块(想想:10 GB)时,对整个文件进行签名的过程可能会很耗时,因为通常签名中的数据会以某种方式被封装。但是,您可以在将数据块流式传输到客户端时计算哈希值,然后对其进行签名以验证真实性。这样,数据是未签名和未加密的,但客户端可以自己生成哈希(在接收时),然后根据“签名哈希”消息验证它。
    • 那么Sign(hash)和Sign(data)是同一个进程吗? Sign(object obj) { var hash = CalculateHash(obj); var hashAndPadding = hash + CreatePadding(); return Sign(hashAndPadding); } 这是无限递归。
    • @nr-91 Em 没有。伪代码:Sign(object obj, Key key) { var hash = CalculateHash(obj); var envelope = new Envelope(object, hash, key); return envelope; }
    【解决方案2】:

    The answer given by zaitsman 很好地解释了与您的问题相关的主题,我认为应该是公认的答案,但只是为了帮助将其与您的具体问题联系起来,即为什么加密哈希不会给您相同的结果作为签名哈希(代码中的rsa.SignHash(hashOfData, "SHA256")):

    对哈希进行签名并不只是加密哈希数据——它还加密用于生成哈希的哈希算法的名称(或某些标识符)。没有它,接收者将不知道在计算自己的哈希(正在发送的消息的)以与他们刚刚解密的哈希进行比较时使用什么算法,以验证消息的真实性(当然,这是整点)。

    当您自己加密哈希时(使用rsa.Encrypt(hashOfData, false) 和rsa.Encrypt(hashOfData, true)),您只加密了哈希数据而不是哈希数据的组合和算法标识符("SHA256" 在您的代码中)。换句话说,你加密了不同的数据,所以你得到了不同的(加密的)结果。

    SignHash 调用的返回值匹配rsa.SignData(data, sha256) 返回的值的原因是后一种方法做同样的事情,除了它将散列和散列签名作为一个操作,因此如果您不需要它用于除签名之外的任何目的,则不必将其作为单独的步骤计算。

    来自 MSDN 上的RSACryptoServiceProvider.SignData Method:

    计算指定数据的哈希值并签名。


    另见:Why does SignHash need to know what hash algorithm was used?

    【讨论】:

    • 谢谢!除了您有关标识符的信息外,我还在您链接的线程上找到了Pavel Ognev's response。希望我可以为我的数据创建正确的填充,以便加密数据(通过rsa.Encrypt())匹配签名(通过rsa.SignData())
    猜你喜欢
    • 2021-04-25
    • 2014-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-23
    • 1970-01-01
    • 2022-11-06
    相关资源
    最近更新 更多