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

我的应用程序将获取一组文件并签名。 (我不想签署一个程序集。)有一个.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); 

根据这个答案它不能做( RSACryptoServiceProvider不支持SHA-256),但我希望可能使用不同的库,如Bouncy城​​堡。

我是新来的这个东西,我发现Bouncy Castle很混乱。 我将一个Java应用程序移植到C#,并且我必须使用相同types的encryption来签署这些文件,所以我被卡在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); 

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

RSA + SHA256可以和将工作…

你以后的例子可能不会一直工作,它应该使用哈希algorithm的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; } 

然而,你到达那里,一旦你获得了一个私钥的证书,我们需要重build它。 由于证书创build私钥的方式,这可能是必需的,但我不确定为什么。 无论如何,我们首先导出密钥,然后使用任何你喜欢的中间格式重新导入它,最简单的就是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)); 

一旦完成,我们现在可以签署一个数据如下:

 //Create some data to sign byte[] data = new byte[1024]; //Sign the data byte[] sig = key.SignData(data, CryptoConfig.MapNameToOID("SHA256")); 

最后,validation可以直接与证书的公钥一起完成,而不需要重build,就像我们对私钥所做的那样:

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

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

更好的解决scheme是显式加载“增强”encryption提供程序,如下所示:

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

我决定更改密钥文件以指定相应的encryption服务提供程序 ,从而避免了.NET中的问题。

所以当我用PEM私钥和CRT公共证书创build一个PFX文件时,我这样做如下:

 openssl pkcs12 -export -aes256 -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -inkey priv.pem -in pub.crt -out priv.pfx 

关键部分是-CSP“Microsoft增强型RSA和AESencryption提供者”

-inkey指定私钥文件, -in指定要包含的公用证书。)

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

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

当你使用一个证书来获得你的RSACryptoServiceProvider时,真正关键的是底层的CryptoAPI提供者是什么。 默认情况下,当您使用'makecert'创build证书时,它是“RSA-FULL”,它只支持SHA1哈希进行签名。 您需要新的支持SHA2的“RSA-AES”。

因此,您可以使用其他选项创build证书:-sp“Microsoft增强的RSA和AESencryption提供程序”(或等价的-sy 24),然后您的代码将无需关键的杂事。

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

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

根据这个博客,它应该使用FX 3.5(见下面的注释)。 然而,重要的是要记住,大多数.NETencryption是基于CryptoAPI (即使CNG在最近的FX版本中越来越多地被暴露)。

关键在于CryptoAPIalgorithm支持取决于正在使用的Crypto Service Provider (CSP),并且在Windows版本之间有所不同(即Windows 7上的工作可能不适用于Windows 2000)。

在创buildRSACCryptoServiceProvider实例时,请阅读(从博客条目)的注释,以查看指定AES CSP(而不是默认设置)的可能解决方法。 这似乎对一些人,YMMV。

注意:这对于很多人来说都是令人困惑的,因为所有发布的.NET框架都包含了CryptoAPI无法使用的SHA256的托pipe实现。 FWIW 单声道不受这些问题;-)

以下是我如何签名一个string,而不必修改证书(Microsoft增强的RSA和AESencryption提供程序)。

  byte[] certificate = File.ReadAllBytes(@"C:\Users\AwesomeUser\Desktop\Test\ServerCertificate.pfx"); X509Certificate2 cert2 = new X509Certificate2(certificate, string.Empty, X509KeyStorageFlags.Exportable); string stringToBeSigned = "This is a string to be signed"; SHA256Managed shHash = new SHA256Managed(); byte[] computedHash = shHash.ComputeHash(Encoding.Default.GetBytes(stringToBeSigned)); var certifiedRSACryptoServiceProvider = cert2.PrivateKey as RSACryptoServiceProvider; RSACryptoServiceProvider defaultRSACryptoServiceProvider = new RSACryptoServiceProvider(); defaultRSACryptoServiceProvider.ImportParameters(certifiedRSACryptoServiceProvider.ExportParameters(true)); byte[] signedHashValue = defaultRSACryptoServiceProvider.SignData(computedHash, "SHA256"); string signature = Convert.ToBase64String(signedHashValue); Console.WriteLine("Signature : {0}", signature); RSACryptoServiceProvider publicCertifiedRSACryptoServiceProvider = cert2.PublicKey.Key as RSACryptoServiceProvider; bool verify = publicCertifiedRSACryptoServiceProvider.VerifyData(computedHash, "SHA256", signedHashValue); Console.WriteLine("Verification result : {0}", verify); 

当我使用的证书不在用户/计算机证书存储中时,我发现.NET中的类似问题是使用了错误的私钥(或者它是否是错误的错误?我不记得)。 将它安装到存储中解决了我的scheme的问题,事情就像预期的那样开始工作 – 也许你可以尝试一下。