【问题标题】:"Invalid type specified." exception when signing using CMS with a certificate in .NET Core 3.1“指定的类型无效。”在 .NET Core 3.1 中使用带有证书的 CMS 进行签名时出现异常
【发布时间】:2020-12-20 17:31:48
【问题描述】:

我正在尝试使用知道其数据 (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;
    }
}

我在做什么:

我从用于签署证书 (certData) 的同一个 CSP 容器加载 RSACryptoServiceProvider 以获取带有私钥的证书。

上面的代码在ComputeSignature调用处抛出了WindowsCryptographicException类型的异常:

指定的类型无效。

堆栈跟踪:

在 Internal.Cryptography.Pal.Windows.HelpersWindows.GetProvParameters(SafeProvOrNCryptKeyHandle 处理)在 Internal.Cryptography.Pal.Windows.PkcsPalWindows.GetPrivateKey[T](X509Certificate2 证书,布尔静默,布尔 preferNCrypt)在 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, HashAlgorithmName hashAlgorithmName, X509Certificate2 证书,非对称算法密钥,布尔静默,Oid&oid, 只读存储器1& signatureValue) at System.Security.Cryptography.Pkcs.CmsSigner.Sign(ReadOnlyMemory1 数据,字符串 contentTypeOid,布尔无声, X509Certificate2Collection&chainCerts)在 System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner 签名者,布尔无声)在 System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner 签名者)在 Program.Sign(字符串提供者名称,字符串容器名称, 字节[] certData) 在 D:\me\Projects\ConsoleTest\ConsoleTest\Program.cs: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");

【问题讨论】:

  • 您好像在使用SHA-1 对数据进行签名,可能该算法已被弃用?
  • 我也试过 SHA512/256,同样的例外
  • 您解决了这个问题吗?
  • 我们切换到 .NET 框架,没有深入研究它

标签: .net-core cryptography rsa x509 pkcs#7


【解决方案1】:

您将提供程序类型硬编码为 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;
    }
}

【讨论】:

  • “类型并不总是 case 1”是什么意思?你的意思是它可能有不同的值(.NET FX&Core)?我只是确保它在 .NET Core 中是“1”。为了确保密钥不是问题,我用它(rsa.SignData(..))签名并且它有效,还有关于代码块,抱歉没有说明:我从用于签名的相同 CSP 参数分配证书其私钥它在创建证书时,因此RSACryptoServiceProvider 密钥是证书的关联密钥。
  • 是的,提供者类型很多:docs.microsoft.com/en-us/dotnet/api/…
  • 我正在使用类型为 1 的提供商“Utimaco CryptoServer CSP”
  • 我的意思是,您不能在代码中硬编码提供程序类型以使其更通用。您是否尝试过更新代码 sn-p?我相信,它应该可以工作,因为我消除了与 CNG 不兼容的 RSACryptoServiceProvider 引用。
  • 您能否详细说明您的解决方案?它似乎与我的代码(虚拟证书之一)没有什么不同,您使用的 req 是用 rsaKey 签名的
猜你喜欢
  • 2020-08-30
  • 1970-01-01
  • 2013-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
相关资源
最近更新 更多