【发布时间】:2015-10-11 18:16:15
【问题描述】:
我关注this great tutorial 使用 .NET 对数据进行数字签名/验证。我修改了该示例代码以使用 SHA256 并点击“指定的算法无效”异常,这导致我在 .NET 4.0 中使用 SHA256 对数据进行签名this SO question。
该帖子中的One of the answers 帮助我弄清楚了如何通过显式加载支持 SHA256 的加密提供程序而不依赖可导出的私钥来正确生成数字签名(请参阅以下方法底部的代码,其中 RSACryptoServiceProvider 已构建):
static string mKeyContainerName;
static byte[] SignText(string text, string publicCertPath)
{
// Access Personal (MY) certificate store of current user
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
// Load the certificate we'll use to verify the signature from a file.
X509Certificate2 publicCert = new X509Certificate2(publicCertPath);
publicCert.Verify();
string publicHash = publicCert.GetCertHashString();
// Find the certificate we'll use to sign
X509Certificate2 privateCert = null;
foreach(X509Certificate2 cert in store.Certificates)
{
if(cert.GetCertHashString() == publicHash)
{
// We found it. Get its associated private key
privateCert = cert;
break;
}
}
store.Close();
if(privateCert == null)
{
throw new Exception("No valid private cert was found");
}
// Hash the string
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
SHA256Managed sha256 = new SHA256Managed();
byte[] hash = sha256.ComputeHash(data);
// The basic crypto provider only supports SHA-1.
// Force Enhanced RSA and AES Cryptographic Provider which supports SHA-256.
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)privateCert.PrivateKey;
var enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
mKeyContainerName = csp.CspKeyContainerInfo.KeyContainerName;
var cspparams = new CspParameters
(
enhCsp.ProviderType, enhCsp.ProviderName, mKeyContainerName
);
csp = new RSACryptoServiceProvider(cspparams);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA256"));
}
值得注意的是,我使用的是来自 makecert.exe 的自签名证书。根据同一篇文章中的another answer,如果我在 makercert.exe 中包含正确的 -sp 或 -sy 标志,这些问题就不会发生。然而,即使在指定了这些标志中的任何一个之后(当前使用 -sy 24),我仍然必须执行解决方法。
此实现与该帖子中的accepted answer 略有不同(同样,因为我们的私钥不可导出)。但该答案确实表明无需显式加载支持 SHA256 的加密提供程序即可完成验证。因此,在返回上述方法之前,我应该能够做到这一点:
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)privateCert.PrivateKey;
var enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
mKeyContainerName = csp.CspKeyContainerInfo.KeyContainerName;
var cspparams = new CspParameters
(
enhCsp.ProviderType, enhCsp.ProviderName, mKeyContainerName
);
csp = new RSACryptoServiceProvider(cspparams);
// Sign the hash
byte[] signature = csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA256"));
// Test to verify the signed hash with public cert
csp = (RSACryptoServiceProvider)publicCert.PublicKey.Key;
if (!csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA256"), signature))
throw new CryptographicException();
return signature;
但是,这不会验证(顺便说一句,我已经尝试过 SignData/VerifyData 和 SignHash/VerifyHash)。我可以验证它的唯一方法是我是否再次显式加载支持 SHA256 的加密提供程序。不幸的是,从公共证书构造的 CspKeyContainerInfo 的 KeyContainerName 成员始终为空。因此,我可以获得数据进行验证的唯一方法是我是否缓存(或硬编码)私钥的 KeyContainerName。因此上述方法中的 mKeyContainerName 字段和下面的 sn-p 的原因:
// Test to verify the signed hash with public cert
csp = (RSACryptoServiceProvider)publicCert.PublicKey.Key;
enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
cspparams = new CspParameters
(
enhCsp.ProviderType, enhCsp.ProviderName, mKeyContainerName
);
csp = new RSACryptoServiceProvider(cspparams);
if (!csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA256"), signature))
throw new CryptographicException();
这确实可以验证,但我不喜欢对私钥的 KeyContainerName 进行硬编码。在进行验证的机器上,私钥将不可用。
有谁知道更好的方法来实现这一点?谢谢!
【问题讨论】:
标签: c# .net hash rsa digital-signature