【问题标题】:DSA generates different signatures with the same dataDSA 使用相同的数据生成不同的签名
【发布时间】:2014-01-01 11:09:55
【问题描述】:

我正在使用this MSDN article 中给出的关于DSACryptoServiceProvider 类的示例。问题是我每次运行代码时都会得到不同的签名。

我尝试了 OpenSSL 并没有遇到这个问题,但这次我需要使用 System.Security.Cryptography。

这是一些源代码:

这是要签名的哈希值

byte[] HashValue =
        {
            59, 4, 248, 102, 77, 97, 142, 201,
            210, 12, 224, 93, 25, 41, 100, 197,
            213, 134, 130, 135
        };

这就是问题所在

 // The value to hold the signed value.
 byte[] SignedHashValue1 = DSASignHash(HashValue, privateKeyInfo, "SHA1");
 byte[] SignedHashValue2 = DSASignHash(HashValue, privateKeyInfo, "SHA1");

我使用调试器发现 SignedHashValue1 不等于 SignedHashValue2


文章代码:

using System;
using System.Security.Cryptography;

public class DSACSPSample
{
    public static void Main()
    {
        try
        {
            DSAParameters privateKeyInfo;
            DSAParameters publicKeyInfo;

            // Create a new instance of DSACryptoServiceProvider to generate
            // a new key pair.
            using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider())
            {
                privateKeyInfo = DSA.ExportParameters(true);
                publicKeyInfo = DSA.ExportParameters(false);
            }

            // The hash value to sign.
            byte[] HashValue =
            {
                59, 4, 248, 102, 77, 97, 142, 201,
                210, 12, 224, 93, 25, 41, 100, 197,
                213, 134, 130, 135
            };

            // The value to hold the signed value.
            byte[] SignedHashValue = DSASignHash(HashValue, privateKeyInfo, "SHA1");

            // Verify the hash and display the results.
            bool verified = DSAVerifyHash(HashValue, SignedHashValue, publicKeyInfo, "SHA1");

            if (verified)
            {
                Console.WriteLine("The hash value was verified.");
            }
            else
            {
                Console.WriteLine("The hash value was not verified.");
            }
        }
        catch (ArgumentNullException e)
        {
            Console.WriteLine(e.Message);
        }
    }

    public static byte[] DSASignHash(byte[] HashToSign, DSAParameters DSAKeyInfo,
        string HashAlg)
    {
        byte[] sig = null;

        try
        {
            // Create a new instance of DSACryptoServiceProvider.
            using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider())
            {
                // Import the key information.
                DSA.ImportParameters(DSAKeyInfo);

                // Create an DSASignatureFormatter object and pass it the
                // DSACryptoServiceProvider to transfer the private key.
                DSASignatureFormatter DSAFormatter = new DSASignatureFormatter(DSA);

                // Set the hash algorithm to the passed value.
                DSAFormatter.SetHashAlgorithm(HashAlg);

                // Create a signature for HashValue and return it.
                sig = DSAFormatter.CreateSignature(HashToSign);
            }
        }
        catch (CryptographicException e)
        {
            Console.WriteLine(e.Message);
        }

        return sig;
    }

    public static bool DSAVerifyHash(byte[] HashValue, byte[] SignedHashValue,
        DSAParameters DSAKeyInfo, string HashAlg)
    {
        bool verified = false;

        try
        {
            // Create a new instance of DSACryptoServiceProvider.
            using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider())
            {
                // Import the key information.
                DSA.ImportParameters(DSAKeyInfo);

                // Create an DSASignatureDeformatter object and pass it the
                // DSACryptoServiceProvider to transfer the private key.
                DSASignatureDeformatter DSADeformatter = new DSASignatureDeformatter(DSA);

                // Set the hash algorithm to the passed value.
                DSADeformatter.SetHashAlgorithm(HashAlg);

                // Verify signature and return the result.
                verified = DSADeformatter.VerifySignature(HashValue, SignedHashValue);
            }
        }
        catch (CryptographicException e)
        {
            Console.WriteLine(e.Message);
        }

        return verified;
    }
}

【问题讨论】:

标签: c# security cryptography digital-signature dsa


【解决方案1】:

如果您查看 DSA 的工作原理(例如在 Wikipedia 上),您会发现生成签名的第一步是选择一个随机值:

为每条消息生成一个随机值 k,其中 0

稍后你会发现这种随机性是必要的:

对于 DSA,随机签名值 k 的熵、保密性和唯一性至关重要。违反这三个要求中的任何一个都可以将整个私钥泄露给攻击者,这一点非常重要。使用相同的值两次(即使对 k 保密)、使用一个可预测的值,或者在多个签名中的每一个中泄露一些 k 位,都足以破坏 DSA。

随后提到了一个非常突出的破坏 ECDSA 的案例(它源自 DSA,但适用于椭圆曲线)。

因此,您应该庆幸自己从未得到相同的签名。否则你的私钥就会受到威胁。

【讨论】:

  • 有一些方法可以确定性地生成 DSA 签名。基本思想是使用消息的哈希值和私钥作为 k。就我个人而言,我非常喜欢那些确定性的变体。
  • 有,是的,但你不能指望在某些操作系统或 sdk 中实现的算法来做到这一点。如果他们这样做,他们的方案可能与您的不同。本质上,您所拥有的只是要求随机数的算法的描述。
  • @CodesInChaos 我不明白每条消息的关键问题。如果我们为每个消息生成不同的密钥而不是私钥,那么如何验证签名呢?如果同一条消息被签名两次导致 2 个不同的哈希值,那么如何仅使用相同的公钥对其进行验证?使困惑!请帮忙!
  • @IbrahimNadir CodesInChaos 并没有说每条消息都应该有不同的 key。他只是指出,每个消息的随机值 k 可以确定性方式创建,只要 A 这种确定性方式不能被其他人复制并且 B 对于不同的消息,您(几乎)总是得到不同的值。私钥加消息的哈希具有这些属性,没有私钥的其他人无法复制它,并且不同的消息导致不同的值,除非很少发生冲突。
  • @mkl 感谢您的回复。我只是想澄清一下。假设我们有相同的消息,并且我们使用两个不同的随机消息值 k 来创建同一消息的两个不同的哈希值。它们将如何在接收端进行验证?是否是数学以某种方式验证了它,因为我们将哈希与消息一起发送。我们从不发送每条消息的值 k。只是脑子里一片混乱。你能澄清一下吗?
【解决方案2】:

AFAIK,它每次都在生成一个新的密钥对,所以签名应该不同吧?

        // Create a new instance of DSACryptoServiceProvider to generate
        // a new key pair.
        using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider())
        {
            privateKeyInfo = DSA.ExportParameters(true);
            publicKeyInfo = DSA.ExportParameters(false);
        }

您不应该每次都保存密钥对并加载相同的密钥对以获得相同的结果吗? 见How to store/retrieve RSA public/private key

【讨论】:

  • 您能否尝试在byte[] SignedHashValue = DSASignHash(HashValue, privateKeyInfo, "SHA1"); 之后添加byte[] SignedHashValue1 = DSASignHash(HashValue, privateKeyInfo, "SHA1"); 并进行比较
  • 它在同一个调试会话中给出不同的签名
  • 我不熟悉 DSA 算法,所以我不能说如果给定相同的 HashValue,它仍然可以为它验证不同的 SignedHashValue。
  • 我不像 RSA 那样熟悉 DSA,但我知道某些 RSA 签名方案包含随机数据,因此结果会总是不同。
  • DSA(就像 ECDSA 一样)在签名创建期间要求用户使用随机值。
猜你喜欢
  • 2023-02-01
  • 2013-12-07
  • 2017-07-24
  • 1970-01-01
  • 1970-01-01
  • 2012-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多