问题描述
我正在尝试使用知道其数据 (byte[]) 和关联密钥的证书或虚拟证书对 CMS 消息进行签名。 该代码在 .NET Framework 中工作,但在 .NET Core 3.1 中失败
public static byte[] Sign(string providerName,string containerName,byte[] certData)
{
try
{
ContentInfo contentInfo = new ContentInfo(new Oid("1.2.840.113549.1.7.1"),new byte[] { 1,2,14 });
var rsaKey = new RSACryptoServiceProvider(new CspParameters
{
ProviderName = providerName,//Utimaco CryptoServer CSP
ProviderType = 1,KeyNumber = (int)KeyNumber.Signature,Flags = CspProviderFlags.UseExistingKey,KeyContainerName = containerName
});
X509Certificate2 certificate = new X509Certificate2(certData);
certificate = certificate.CopyWithPrivateKey(rsaKey);
var signer = new CmsSigner(certificate)
{
DigestAlgorithm = new Oid("1.3.14.3.2.26")
};
signer.SignedAttributes.Add(new Pkcs9SigningTime());
var signedCms = new SignedCms(contentInfo,true);
signedCms.ComputeSignature(signer);
var signedData = signedCms.Encode();
return signedData;
}
catch (Exception ex)
{
return null;
}
}
我在做什么:
我从用于签署证书 (RSACryptoServiceProvider
) 的同一个 CSP 容器加载 certData
以获取带有私钥的证书。
上面的代码在调用 WindowsCryptographicException
时抛出类型为 ComputeSignature
的异常:
指定的类型无效。
堆栈跟踪:
在
Internal.Cryptography.Pal.Windows.HelpersWindows.GetProvParameters(SafeProvOrNCryptKeyHandle
处理)在
Internal.Cryptography.Pal.Windows.PkcsPalWindows.GetPrivateKey[T](X509Certificate2
证书,布尔静音,布尔首选NCrypt)在
Internal.Cryptography.Pal.Windows.PkcsPalWindows.GetPrivateKeyForSigning[T](X509Certificate2
证书,布尔静音)在
System.Security.Cryptography.Pkcs.CmsSignature.RSAPkcs1CmsSignature.Sign(ReadOnlySpan1 dataHash,HashAlgorithmName hashAlgorithmName,X509Certificate2 certificate,AsymmetricAlgorithm key,Boolean silent,Oid& signatureAlgorithm,Byte[]& signatureValue) at System.Security.Cryptography.Pkcs.CmsSignature.Sign(ReadOnlySpan
1
dataHash,X509Certificate2
证书、非对称算法密钥、布尔静默、Oid&oid、
只读内存1& signatureValue) at System.Security.Cryptography.Pkcs.CmsSigner.Sign(ReadOnlyMemory
1
数据,字符串 contentTypeOid,布尔静音,
X509Certificate2Collection&chainCerts) at
System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner
签名者,布尔无声)在
System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner
签名者) 在 Program.Sign(String providerName,String containerName,字节[] certData) 中
D:\me\Projects\ConsoleTest\ConsoleTest\Program.cs:line 239
此外,使用虚拟证书也不起作用:
CertificateRequest req = new CertificateRequest(
"CN=CMS Signer Dummy Certificate",rsa,HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1);
DateTimeOffset now = DateTimeOffset.UtcNow;
using (X509Certificate2 cert = req.CreateSelfSigned(now,now.AddYears(1)))
{
CmsSigner signer = new CmsSigner(cert);
...
}
为了确保密钥没有问题,我用它签名 (rsaKey.SignData(...)
) 并且它可以工作。
当我使用文件中的证书时它工作的唯一情况,我的机器上有:
X509Certificate2 certificate = new X509Certificate2(@"C:\MyCert.pfx","123456");
解决方法
您将提供程序类型硬编码为 1:
ProviderType = 1,
情况并非总是如此,您刚刚遇到了这种情况。您应该传递实际的提供程序类型。我怀疑您的密钥存储在密钥存储提供程序(而不是传统 CSP)中,并且无法从 RSACryptoServiceProvider
类访问。这是另一种例外的可能性。
这个代码块对我来说是 0 意义:
var rsaKey = new RSACryptoServiceProvider(new CspParameters
{
ProviderName = providerName,ProviderType = 1,KeyNumber = (int)KeyNumber.Signature,Flags = CspProviderFlags.UseExistingKey,KeyContainerName = containerName
});
X509Certificate2 certificate = new X509Certificate2(certData);
certificate = certificate.CopyWithPrivateKey(rsaKey);
你在这里尝试完成的事情对我来说很难理解。实际上,您不需要此代码块。将带有 HasPrivateKey = true
的证书直接传递给方法并使用它,即:
public static byte[] Sign(X509Certificate2 certificate)
{
try
{
ContentInfo contentInfo = new ContentInfo(new Oid("1.2.840.113549.1.7.1"),new byte[] { 1,2,14 });
var signer = new CmsSigner(certificate)
{
DigestAlgorithm = new Oid("1.3.14.3.2.26")
};
signer.SignedAttributes.Add(new Pkcs9SigningTime());
var signedCms = new SignedCms(contentInfo,true);
signedCms.ComputeSignature(signer);
var signedData = signedCms.Encode();
return signedData;
}
catch (Exception ex)
{
return null;
}
}