密钥不存在 - 使用自签名 ECDSA 证书

Key does not exist - using self signed ECDSA certificate

我对 XML 邮件进行数字签名时遇到问题。要求规定 ECDSA 证书要与特定曲线一起使用。这意味着如果不创建 SignatureDescriptionAsymmetricSignatureFormatterAsymmetricSignatureDeformatter.[=21 的自定义实现,SignedXml.ComputeSignature(...) 方法提供的 RSA 和 DSA 功能将无法工作=]

也就是说,在没有发布大量代码的情况下,我创建了一个简单的函数,它应该获取证书并使用其 public 密钥使用 ECDsaCng class 对数据进行签名这就是我用来创建上述自定义实现的方法。

发生的情况是,即使使用我的自定义实现,我也会抛出异常“System.Security.Cryptography.CryptographicException: Key does not exist.”。但是,如果我只是创建一个 CngKey 而不是导入它,它就可以工作。据我所见,这个异常被抛出了调用 internal static extern ErrorCode NCryptSignHash(...) 方法的代码,它位于 ncrypt.dll

所以我的问题是为什么抛出异常“System.Security.Cryptography.CryptographicException: Key does not exist.”以及如何解决它。

[TestMethod]
public void CreateSignatureTest()
{
  var request = "Some xml goes here...";

  var store = new X509Store(StoreLocation.LocalMachine);
  store.Open(OpenFlags.ReadOnly);
  var certificates = store.Certificates;

  var applicableCertificate = certificates.Cast<X509Certificate2>()
     .FirstOrDefault(certificate => certificate.Subject.Contains("ECDSA_CERT_NAME"));

  var encryptingKey = (ECDsaCng)applicableCertificate.GetECDsaPublicKey();

  var exported = encryptingKey.Key.Export(CngKeyBlobFormat.EccPublicBlob);
  var creationParameters = new CngKeyCreationParameters
  {
    ExportPolicy = CngExportPolicies.AllowPlaintextExport
  };

  using (CngKey objCngKey = CngKey.Import(exported, CngKeyBlobFormat.EccPublicBlob)) // This will throw a Key Does Not Exist Exception
  //using (CngKey objCngKey = CngKey.Create(CngAlgorithm.ECDsaP256, null, creationParameters)) // This works...
  {
    //'Convert String to be signed to a byte array
    var data = Encoding.Default.GetBytes(request);

    //'Create a ECDsaCng Object
    var ecdsa = new ECDsaCng(objCngKey);

    //'Sign the string
    var bSignature = ecdsa.SignData(data);

    //'Convert Signature to Base64 string for better reading
    var sSignature = Convert.ToBase64String(bSignature);
    }
}

您获取了 public 密钥。然后将其导入到一个新容器中并调用 Sign。由于签名需要私钥,因此抛出一个异常,指出私钥字段不存在。

你为什么要克隆这个对象?只需调用 cert.GetECDsaPrivateKey() 并使用该对象即可。