简体   繁体   English

证书签名在服务器上产生不同的签名

[英]Certificate signing produces different signature when on server

I am trying to sign some data using a certificate private key. 我试图使用证书私钥签署一些数据。 The issue I'm finding is that the signature is different depending on if I'm executing it locally or on a server. 我发现的问题是签名是不同的取决于我是在本地还是在服务器上执行它。

I'm using the following code as a test, running under the same user both locally and on the server: 我使用以下代码作为测试,在本地和服务器上的同一用户下运行:

using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace TestSignature
{
    class Program
    {
        static void Main(string[] args)
        {

            var key = SigningKeyFromCertificate(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, "thumbprint");
            var alg = CryptoConfig.MapNameToOID("SHA256");
            var data = Encoding.UTF8.GetBytes("test");
            var sig = key.SignData(data, alg);

            Console.WriteLine(Convert.ToBase64String(sig));

        }

        private static RSACryptoServiceProvider SigningKeyFromCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findType, string findValue)
        {
            X509Store store = new X509Store(storeName, storeLocation);
            store.Open(OpenFlags.ReadOnly);

            var certs = store.Certificates.Find(findType, findValue, false);
            if (certs?.Count > 0)
            {
                var cert = certs[0];
                if (cert.HasPrivateKey)
                {
                    // Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
                    var key = cert.PrivateKey as RSACryptoServiceProvider;
                    var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;
                    var parameters = new CspParameters(enhanced.ProviderType, enhanced.ProviderName, key.CspKeyContainerInfo.UniqueKeyContainerName);
                    return new RSACryptoServiceProvider(parameters);
                }
                else
                {
                    throw new Exception($"No private key access to cert '{findValue}.'");
                }
            }
            else
            {
                throw new Exception($"Cert '{findValue}' not found!");
            }
        }
    }
}

Locally, I get the following signature: 在本地,我得到以下签名:

YUjspKhLl7v3u5VQkh1PfHytMTpEtbAftxOA5v4lmph3B4ssVlZp7KedO5NW9K5L222Kz9Ik9/55NirS0cNCz/cDhEFRtD4daJ9qLRuM8oD5hCj6Jt9Vc6WeS2he+Cqfoylnv4V9plfi1xw8y7EyAf4C77BGkXOdyP5wyz2Xubo=

On the server, I get this one instead: 在服务器上,我得到了这个:

u1RUDwbBlUpOgNNkAjXhYEWfVLGpMOa0vEfm6PUkB4y9PYBk1lDmCAp+488ta+ipbTdSDLM9btRqsQfZ7JlIn/dIBw9t5K63Y7dcDcc7gDLE1+umLJ7EincMcdwUv3YQ0zCvzc9RrP0jKJManV1ptQNnODpMktGYAq1KmJb9aTY=

Any idea of what could be different? 什么可能有所不同? I would think, with the same certificate, the same code, and the same data, the signature should be the same. 我认为,使用相同的证书,相同的代码和相同的数据,签名应该是相同的。

(The example is written in C# 4.5.2.) (该示例使用C#4.5.2编写。)

You have some code to reopen the CAPI key handle under PROV_RSA_AES : 您有一些代码可以重新打开PROV_RSA_AES下的CAPI密钥句柄:

// Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
var key = cert.PrivateKey as RSACryptoServiceProvider;
var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;

var parameters = new CspParameters(
    enhanced.ProviderType,
    enhanced.ProviderName,
    key.CspKeyContainerInfo.UniqueKeyContainerName);

return new RSACryptoServiceProvider(parameters);

But key.CspKeyContainerInfo.UniqueKeyContainerName isn't the name of the key (it's the name of the file on disk where the key lives), so you're opening a brand new key (you're also generating a new ephemeral key just to ask what the default provider is). key.CspKeyContainerInfo.UniqueKeyContainerName不是密钥的名称(它是密钥所在磁盘上文件的名称),因此您打开一个全新的密钥(您还要生成一个新的临时密钥)问一下默认的提供者是什么)。 Since it's a named key it persists, and subsequent application executions resolve to the same key -- but a different "same" key on each computer. 由于它是一个命名密钥,它会持续存在,后续的应用程序执行将解析为相同的密钥 - 但每台计算机上的密钥不同“相同”。

A more stable way of reopening the key is 更加稳定的重新打开密钥的方法是

var cspParameters = new CspParameters
{
    KeyContainerName = foo.CspKeyContainerInfo.KeyContainerName,
    Flags = CspProviderFlags.UseExistingKey,
};

(since the provider type and name aren't specified they will use the defaults, and by saying UseExistingKey you get an exception if you reference a key that doesn't exist). (由于未指定提供程序类型和名称,因此它们将使用默认值,并且如果引用不存在的键,则说明UseExistingKey会出现异常)。


That said, the easiest fix is to stop using RSACryptoServiceProvider . 也就是说,最简单的解决方法是停止使用RSACryptoServiceProvider .NET Framework 4.6 (and .NET Core 1.0) have a(n extension) method on X509Certificate2 , GetRSAPrivateKey() , it returns an RSA (which you should avoid casting) which is usually RSACng (on Windows), but may be RSACryptoServiceProvider if only CAPI had a driver required for a HSM, and may be some other RSA in the future. .NET Framework 4.6(和.NET Core 1.0)在X509Certificate2GetRSAPrivateKey()上有一个(n扩展名)方法,它返回一个RSA (你应该避免强制转换),它通常是RSACng (在Windows上),但如果是RSACryptoServiceProvider则可能是只有CAPI有HSM所需的驱动程序,未来可能还有其他一些RSA。 Since RSACng handles SHA-2 better there's almost never a need to "reopen" the return object (even if it's RSACryptoServiceProvider , and even if the type isn't PROV_RSA_AES (24), that doesn't mean the HSM will fail to do SHA-2). 由于RSACng更好地处理SHA-2,几乎从不需要“重新打开”返回对象(即使它是RSACryptoServiceProvider ,即使类型不是PROV_RSA_AES (24),也不意味着HSM将无法执行SHA -2)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM