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



我使用的是MSDN文章中给出的关于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;
    }
}

如果你看看DSA是如何工作的(例如在维基百科上),你会发现生成签名的第一步是选择一个随机值:

生成每个消息的随机值k,其中0<k<q

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

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

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

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

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

你不应该保存密钥对,每次加载相同的密钥对,以每次获得相同的结果吗?请参阅如何存储/检索RSA公钥/私钥

最新更新