【发布时间】:2020-01-18 03:18:34
【问题描述】:
我正在尝试将一些较旧的 CAPI 代码转换为使用 CNG,特别是为了使用临时私钥来补充证书。 (据我了解,CAPI 不支持。)
我们使用证书的私钥 (PK) 对数据进行签名。我希望这两种实现能够产生兼容的输出,但令我惊讶的是它们没有。
// Hydrate a cert with PK. Make PK a file-based key so we can get the container and use CAPI.
// (Note, although .NET Framework 4.8, the LangVersion is 8.0 so I can use using.)
using var cert = new X509Certificate2(testCertData, "passwd", X509KeyStorageFlags.MachineKeySet);
// Some arbitrary data to sign.
byte[] data = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
byte[] signatureCsp, signatureCng;
// Sign with CAPI. This is what we're doing today.
// The cert PK's crypto provider doesn't support SHA256, so we build a different one.
var csp = (RSACryptoServiceProvider)cert.PrivateKey;
var cp = new CspParameters{
KeyContainerName = csp.CspKeyContainerInfo.KeyContainerName,
KeyNumber = csp.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2,
};
using (var rsaCsp = new RSACryptoServiceProvider(cp)) {
signatureCsp = rsaCsp.Sign(data);
}
// Sign with CNG. This is what I want to do.
using (var rsaCng = cert.GetRSAPrivateKey()) {
signatureCng = rsaCng.Sign(data);
}
// The signatures are different. In fact they're different lengths, 128 and 256 bytes respectively.
Console.WriteLine(Convert.ToBase64String(signatureCsp));
Console.WriteLine(Convert.ToBase64String(signatureCng));
// But maybe they're compatible? Let's see if code which verifies the first can verify the second.
using (var provider = new RSACryptoServiceProvider(cp)) {
var verifiedCsp = provider.Verify(data, signatureCsp);
var verifiedCng = provider.Verify(data, signatureCng);
Console.WriteLine("RSACryptoServiceProvider verified: {0}", verifiedCsp);
Console.WriteLine("RSACng signature verified: {0}", verifiedCng); // Nope. False.
}
为统一起见,以上使用以下扩展方法,将这两种情况视为抽象 RSA 基类型的实现,并具有相同的加密参数。
public static byte[] Sign(this RSA rsa, byte[] buffer) {
return rsa.SignData(buffer, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
public static bool Verify(this RSA rsa, byte[] buffer, byte[] signature) {
return rsa.VerifyData(buffer, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
我需要做些什么来使用证书的 CNG 密钥对数据进行签名,以便输出可以被 CAPI 密钥生成的签名的当前消费者验证?
【问题讨论】:
-
我认为这一切都归结为您重新打开了
rsaCsp的密钥(只需使用csp),而您丢失了机器密钥位。因此,您对密钥的“重新打开”使新的密钥具有默认大小(1024 位)。 -
@bartonjs。不幸的是,这是评论中解释的必要步骤。与导入的证书关联的 CSP 不能使用 SHA256,因此需要使用更新的提供程序打开密钥。我见过明确设置 CspParameters.ProviderName 的示例,但实际上我发现这不是必需的。 AFAIK 这不会产生新的密钥。一方面,每次执行都会产生相同的签名;如果每次运行都重新生成密钥,那么每次运行都会产生不同的签名。
-
很公平,但您需要在 CspParameters 中设置 MachineKey 标志。您当前的代码在用户范围内打开或创建同名密钥。
-
确实你是对的!谢谢!我想如果有一个额外的文件,它会在 CSP 被处理时被清理,但不是。所以我添加了
Flags = CspProviderFlags.UseMachineKeyStore。关于问题的关键,如何使用 RSA CNG 以与此 CAPI 签名方法兼容的方式进行签名的任何建议? -
除了缺少 MachineKey 标志外,其他一切看起来都正确。如果它仍然无法正常工作,那么最好了解一下您所处的状态。
标签: c# rsa sign x509certificate2 cng