DSA使用相同的数据生成不同的签名

时间:2013-12-13 15:55:41

标签: c# security cryptography digital-signature dsa

我正在使用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;
    }
}

2 个答案:

答案 0 :(得分:4)

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

  

生成随机的每消息值k,其中0 <0。 k&lt; q

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

  

使用DSA,随机签名值k的熵,保密性和唯一性是至关重要的。至关重要的是,违反这三个要求中的任何一个都可以向攻击者揭示整个私钥。使用相同的值两次(即使在保持k秘密的情况下),使用可预测的值,或者在几个签名的每一个中泄漏甚至几个k位,都足以打破DSA。

此后提到了一个非常突出的破坏ECDSA的案例(由DSA衍生但在椭圆曲线上工作)。

因此,您应该感到高兴,因为您从未获得过相同的签名。否则你的私钥就会受到威胁。

答案 1 :(得分:1)

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