如何使用RSA和SHA256与.NET签署文件?

sco*_*ott 43 .net c# openssl cryptography bouncycastle

我的应用程序将采用一组文件并对其进行签名.(我不是要尝试签署程序集.)有一个.p12文件,我从中获取私钥.

这是我试图使用的代码,但我得到了一个System.Security.Cryptography.CryptographicException "Invalid algorithm specified.".

X509Certificate pXCert = new X509Certificate2(@"keyStore.p12", "password");
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)pXCert.PrivateKey;
string id = CryptoConfig.MapNameToOID("SHA256");
return csp.SignData(File.ReadAllBytes(filePath), id);
Run Code Online (Sandbox Code Playgroud)

根据这个答案,它无法完成(RSACryptoServiceProvider不支持SHA-256),但我希望可以使用不同的库,如Bouncy Castle.

我是新手,我发现Bouncy Castle非常混乱.我正在将一个Java应用程序移植到C#,我必须使用相同类型的加密来签名文件,所以我坚持使用RSA + SHA256.

我怎么能用Bouncy Castle,OpenSSL.NET,Security.Cryptography或其他我没有听说过的第三方库来做到这一点?我假设,如果它可以在Java中完成,那么它可以在C#中完成.

更新:

这是我从poupou的anwser链接中得到的

        X509Certificate2 cert = new X509Certificate2(KeyStoreFile, password");
        RSACryptoServiceProvider rsacsp = (RSACryptoServiceProvider)cert.PrivateKey;
        CspParameters cspParam = new CspParameters();
        cspParam.KeyContainerName = rsacsp.CspKeyContainerInfo.KeyContainerName;
        cspParam.KeyNumber = rsacsp.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2;
        RSACryptoServiceProvider aescsp = new RSACryptoServiceProvider(cspParam);
        aescsp.PersistKeyInCsp = false;
        byte[] signed = aescsp.SignData(File.ReadAllBytes(file), "SHA256");
        bool isValid = aescsp.VerifyData(File.ReadAllBytes(file), "SHA256", signed);
Run Code Online (Sandbox Code Playgroud)

问题是我没有得到与原始工具相同的结果.据我所知,通过读取代码,执行实际签名的CryptoServiceProvider不使用密钥库文件中的PrivateKey.那是对的吗?

csh*_*net 56

RSA + SHA256可以并且可以工作......

您的后续示例可能无法一直运行,它应该使用哈希算法的OID,而不是它的名称.根据您的第一个示例,这是通过调用[CryptoConfig.MapNameToOID](AlgorithmName)1获得的,其中AlgorithmName是您提供的(即"SHA256").

首先,您需要的是带有私钥的证书.我通常通过使用公钥文件(.cer)来识别私钥,从LocalMachine或CurrentUser商店读取我的,然后枚举证书并匹配哈希...

X509Certificate2 publicCert = new X509Certificate2(@"C:\mycertificate.cer");

//Fetch private key from the local machine store
X509Certificate2 privateCert = null;
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach( X509Certificate2 cert in store.Certificates)
{
    if (cert.GetCertHashString() == publicCert.GetCertHashString())
        privateCert = cert;
}
Run Code Online (Sandbox Code Playgroud)

无论如何,一旦你获得了带私钥的证书,我们就需要重建它.由于证书创建私钥的方式,这可能是必需的,但我不确定为什么.无论如何,我们首先导出密钥,然后使用您喜欢的任何中间格式重新导入它,最简单的是xml:

//Round-trip the key to XML and back, there might be a better way but this works
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));
Run Code Online (Sandbox Code Playgroud)

完成后,我们现在可以签署一条数据,如下所示:

//Create some data to sign
byte[] data = new byte[1024];

//Sign the data
byte[] sig = key.SignData(data, CryptoConfig.MapNameToOID("SHA256"));
Run Code Online (Sandbox Code Playgroud)

最后,验证可以直接使用证书的公钥进行,而不需要像使用私钥那样进行重建:

key = (RSACryptoServiceProvider)publicCert.PublicKey.Key;
if (!key.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), sig))
    throw new CryptographicException();
Run Code Online (Sandbox Code Playgroud)

  • 我还没有看到具有可导出私钥的生产环境.这不是一个好的答案. (14认同)
  • 私钥的导出/导入可能从不支持SHA256的CSP(与证书关联的CSP)切换到支持它的另一个CSP(例如"AES"CSP) (5认同)
  • 我在导出/导入之前和之后序列化了key.CspKeyContainerInfo来比较它们,它确实有不同的提供者类型,从"Microsoft Base Cryptographic Provider v1.0"到"Microsoft Enhanced RSA and AES Cryptographic Provider". (4认同)
  • 这对我不起作用,我得到以下异常,"mscorlib.dll中发生了'System.Security.Cryptography.CryptographicException'类型的未处理异常附加信息:密钥无法在指定状态下使用"来自行:key .FromXmlString(privateCert.PrivateKey.ToXmlString(真)); (2认同)

小智 20

privateKey.toXMLString(true)或privateKey.exportParameters(true)的使用在安全环境中不可用,因为它们需要您的私钥可导出,这不是一个好习惯.

更好的解决方案是显式加载"Enhanced"加密提供程序,如下所示:

// Find my openssl-generated cert from the registry
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, "myapp.com", true);
var certificate = certificates[0];
store.Close();
// Note that this will return a Basic crypto provider, with only SHA-1 support
var privKey = (RSACryptoServiceProvider)certificate.PrivateKey;
// Force use of the Enhanced RSA and AES Cryptographic Provider with openssl-generated SHA256 keys
var enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
var cspparams = new CspParameters(enhCsp.ProviderType, enhCsp.ProviderName, privKey.CspKeyContainerInfo.KeyContainerName);
privKey = new RSACryptoServiceProvider(cspparams);
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,在执行此操作后,您无法使用使用此密钥签名的verifydata的公钥 (3认同)
  • 这似乎产生了明显不正确的哈希.它似乎没有访问原始私钥,因为我没有获得安全弹出窗口. (2认同)
  • 这种方法不起作用,我不知道它是如何有这么多的upvotes.它使用的密钥不是证书中的密钥,因此所有签名都不正确. (2认同)

小智 9

这就是我处理这个问题的方法:

 X509Certificate2 privateCert = new X509Certificate2("certificate.pfx", password, X509KeyStorageFlags.Exportable);

 // This instance can not sign and verify with SHA256:
 RSACryptoServiceProvider privateKey = (RSACryptoServiceProvider)privateCert.PrivateKey;

 // This one can:
 RSACryptoServiceProvider privateKey1 = new RSACryptoServiceProvider();
 privateKey1.ImportParameters(privateKey.ExportParameters(true));

 byte[] data = Encoding.UTF8.GetBytes("Data to be signed"); 

 byte[] signature = privateKey1.SignData(data, "SHA256");

 bool isValid = privateKey1.VerifyData(data, "SHA256", signature);
Run Code Online (Sandbox Code Playgroud)


Tim*_*imo 9

我决定更改密钥文件以指定适当的加密服务提供程序,完全避免.NET中的问题.

因此,当我使用PEM私钥和CRT公共证书创建PFX文件时,我按如下方式执行:

openssl pkcs12 -export -aes256 -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -inkey priv.pem -in pub.crt -out priv.pfx
Run Code Online (Sandbox Code Playgroud)

关键部分是-CSP"Microsoft增强型RSA和AES加密提供商".

(-inkey指定私钥文件并-in指定要合并的公共证书.)

您可能需要根据手头的文件格式调整此值.此页面上的命令行示例可以帮助您:https: //www.sslshopper.com/ssl-converter.html

我在这里找到了这个解决方案:http: //hintdesk.com/c-how-to-fix-invalid-algorithm-specified-when-signing-with-sha256/


Vin*_*dra 6

使用可以在更新的框架上使用它。

    public byte[] GetSignature(byte[] inputData)
    {
        using (var rsa = this.signingCertificate.GetRSAPrivateKey())
        {
            return rsa.SignData(inputData, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        }
    }

    public bool ValidateSignature(byte[] inputData, byte[] signature)
    {
        using (var rsa = this.signingCertificate.GetRSAPublicKey())
        {
            return rsa.VerifyData(inputData, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        }
    }
Run Code Online (Sandbox Code Playgroud)

上面signingCertificate是一个X509Certificate2带有私钥的。此方法不需要您导入任何现有密钥,并且可以在安全的环境中工作。


小智 5

当您使用证书获取RSACryptoServiceProvider时,底层的CryptoAPI提供程序真正重要.默认情况下,当您使用'makecert'创建证书时,它是"RSA-FULL",它仅支持用于签名的SHA1哈希值.您需要支持SHA2的新"RSA-AES".

因此,您可以使用其他选项创建证书:-sp"Microsoft增强型RSA和AES加密提供程序"(或等效的-sy 24),然后您的代码可以在没有关键的杂耍内容的情况下工作.