16

How can I get the public/private keys from an ECC-based X509Certificate2's into CngKey's for use with ECDsaCng and ECDiffieHellmanCng?

I'm currently using RSA 2048 bit key pairs to sign/encrypt stuff. I'm doing this by pulling the certificates from the X509Store where they are securely stored with private keys marked as non-exportable. I would like to convert the current implementation to use ECDSA and ECDH so that I can use smaller key sizes for equivalent security.

I've successfully generated ECC certs using openssl:

  1. openssl ecparam -out private.pem -name prime256v1 -genkey
  2. openssl req -new -key private.pem -x509 -nodes -days 365 -out public.cer
  3. openssl pkcs12 -export -in public.cer -inkey private.pem -out export.pfx

I've successfully installed the above generated certs in to the cert store. I can retrieve them by thumbprint, but the crypto providers for the private and public keys throw "Algorithm not supported" exceptions. Instead, I understand I'm supposed to use ECDsaCng and ECDiffieHellmanCng to sign/encrypt. But these deal in CngKey's.

Bouncy Castle isn't an option because it requires the private keys to be exportable.

CLR Security will return me a CngKey pair via GetCngPrivateKey but it cannot be used with ECDsa because the key returned by CLRSecurity is an ECDH key. Furthermore CLR Security doesn't give me a way to get just the public key from an X509Certificate2 for signature verification (where I don't even have or need the private key of the signer).

Any ideas? I'm at my wits end... Any help would be much appreciated.

4

1 回答 1

8

您需要从证书的公钥 ( certificate.PublicKey.EncodedKeyValue.RawData) 创建 CngKey:

CngKey 包含 8 个附加字节,前 4 个字节(所谓的魔术)用于所用曲线的名称(对于 ECDsa:ECS1、ECS3 或 ECS5,对于 ECDH:ECK1、ECK3、ECK5),最后 4 个是密钥的长度,包括。填充(32、48 或 66)。

证书中公钥的第一个字节被删除(因为对于 ECDSA 公钥,它始终为 0x04)。

因此,例如对于使用 P-256 曲线和 SHA-256 哈希算法的 ECDSA,您将获得长度为 65 字节的公钥。丢弃第一个字节,留下 64 个字节,然后以 4 个字节作为曲线前缀和 4 个字节作为密钥长度前缀,即:

var keyData = certificate.PublicKey.EncodedKeyValue.RawData.Skip(1).ToArray();
var keySize = BitConverter.GetBytes(keyData.Length/2);
var magic = Encoding.ASCII.GetBytes("ECS1").Concat()

var eccPublicBlobValue = magic.Concat(keySize).Concat(keyData).ToArray();

现在你有了公钥(72 字节)来创建 CngKey:

var cngKey = CngKey.Import(eccPublicBlobValue, CngKeyBlobFormat.EccPublicBlob);

var ecdsaCng = new ECDsaCng(cngKey);

您可以验证签名:

return ecdsaCng.VerifyData(encodedBytes, signature);
于 2014-06-15T19:01:18.403 回答