I have an issue with digitally signing an XML message. Requirements dictate that an ECDSA certificate is to be used with a specific curve. Meaning that the RSA and DSA functionality that is provided by the SignedXml.ComputeSignature(...)
method won't work without creating a custom implementation of the SignatureDescription
, AsymmetricSignatureFormatter
and AsymmetricSignatureDeformatter
.
That being said and without posting lots of code I have created a simple function that should take a certificate and using its public key sign the data using the ECDsaCng
class as that is what I have used to create the custom implementation mentioned above.
What happens is, even with my custom implementation, that I get an exception thrown "System.Security.Cryptography.CryptographicException: Key does not exist.
". If however I just create a CngKey
instead of importing it it works. From what I can see this exception is being thrown out of the code that is calling a method on internal static extern ErrorCode NCryptSignHash(...)
which is in the ncrypt.dll
So my question is why an exception is thrown "System.Security.Cryptography.CryptographicException: Key does not exist.
" and how to resolve it.
[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);
}
}
You fetched the public key. Then imported it into a new container and called Sign. Since the private key is required for signing it threw an exception saying that the private key fields are not present.
Why are you cloning the object anyways? Just call cert.GetECDsaPrivateKey()
and use that object.