“指定的类型无效”在 .NET Core 3.1 中使用带有证书的 CMS 进行签名时出现异常

问题描述

我正在尝试使用知道其数据 (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(ReadOnlySpan1 dataHash,X509Certificate2 证书、非对称算法密钥、布尔静默、Oid&oid、 只读内存1& signatureValue) at System.Security.Cryptography.Pkcs.CmsSigner.Sign(ReadOnlyMemory1 数据,字符串 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;
    }
}

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...