Reputation: 3069
I am using:
Here is what I did:
Got a self signed ECC certificate using OpenSSL and steps outlined in the script at https://gist.github.com/sidshetye/4759690 with modifications:
a) Used NIST/P-256 curve over a 256 bit prime field
b) Used SHA-256
Load the certificate from file (generated in previous step) into X509Certificate2 object
Imported the PFX file into windows trust store (for testing). This is successful.
I am stuck at the last step because all the examples that use X509Certificate2 object predominantly use only RSA and I am using ECC certificate. For RSA certificate, there is a GetRSAPublicKey extention method on X509Certificate2 and RSA class has Encrypt method. However there is no such method for ECC certificates.
Next, I stumbled on this post (Load a Certificate Using X509Certificate2 with ECC Public Key) and tried following (even though it appeared bizarre as to why ECC cert public key is being coerced into RSA type):
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key
I got following exception: The certificate key algorithm is not supported.
Next I stumbled on this post (Importing ECC-based certificate from the Windows Certificate Store into CngKey) which basically tried to create CNGKey type and instantiate ECDsaCng with it. However even if I can do it with ECDiffieHellmanCng, there is no Encrypt method on it.
So I am not really sure how can I proceed further to use ECC X509 certificate's public key to encrypt data.
Upvotes: 2
Views: 8454
Reputation: 33088
Asymmetric algorithms have three different purposes (that I know of)
Because RSA encryption is space limited, and was hard for computers in the '90s, RSA encryption's primary use was in "Key Transfer", which is to say that the "encrypted message" was just the symmetric encryption key for DES/3DES (AES not yet having been invented) - https://www.rfc-editor.org/rfc/rfc2313#section-8.
Key agreement (or transfer) schemes always have to be combined with a protocol/scheme to result in an encryption operation. Such schemes include
So what you probably want is ECIES.
Currently (.NET Framework 4.7.1, .NET Core 2.0) there's no support to get an ECDiffieHellman object from a certificate in .NET.
Game over, right? Well, probably not. Unless a certificate carrying an ECDH key explicitly uses the id-ecDH algorithm identifier (vs the more standard id-ecc one) it can be opened as ECDSA. Then, you can coerce that object into being ECDH:
using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
return ECDiffieHellman.Create(ecdsa.ExportParameters(false));
}
(a similar thing can be done for a private key, if the key is exportable, otherwise complex things are required, but you shouldn't need it)
Let's go ahead and carve off the recipient public object:
ECDiffieHellmanPublicKey recipientPublic = GetECDHFromCertificate(cert).PublicKey;
ECCurve curve = recipientPublic.ExportParameters().Curve;
So now we turn to http://www.secg.org/sec1-v2.pdf section 5.1 (Elliptic Curve Integrated Encryption Scheme)
Make an ephemeral key on the right curve.
ECDiffieHellman ephem = ECDiffieHellman.Create(curve);
We decided no.
ECParameters ephemPublicParams = ephem.ExportParameters(false);
int pointLen = ephemPublicParams.Q.X.Length;
byte[] rBar = new byte[pointLen * 2 + 1];
rBar[0] = 0x04;
Buffer.BlockCopy(ephemPublicParams.Q.X, 0, rBar, 1, pointLen);
Buffer.BlockCopy(ephemPublicParams.Q.Y, 0, rBar, 1 + pointLen, pointLen);
Can't directly do this, moving on.
Can't directly do this, moving on.
Since we're in control here, we'll just do 3, 4, 5, and 6 as one thing.
KDF time.
// This is why we picked AES 256, HMAC-SHA-2-256(-256) and SHA-2-256,
// the KDF is dead simple.
byte[] ek = ephem.DeriveKeyFromHash(
recipientPublic,
HashAlgorithmName.SHA256,
null,
new byte[] { 0, 0, 0, 1 });
byte[] mk = ephem.DeriveKeyFromHash(
recipientPublic,
HashAlgorithmName.SHA256,
null,
new byte[] { 0, 0, 0, 2 });
Encrypt stuff.
byte[] em;
// ECIES uses AES with the all zero IV. Since the key is never reused,
// there's not risk in that.
using (Aes aes = Aes.Create())
using (ICryptoTransform encryptor = aes.CreateEncryptor(ek, new byte[16]))
{
if (!encryptor.CanTransformMultipleBlocks)
{
throw new InvalidOperationException();
}
em = encryptor.TransformFinalBlock(message, 0, message.Length);
}
MAC it
byte[] d;
using (HMAC hmac = new HMACSHA256(mk))
{
d = hmac.ComputeHash(em);
}
Finish
// Either
return Tuple.Create(rBar, em, d);
// Or
return rBar.Concat(em).Concat(d).ToArray();
Left as an exercise to the reader.
Upvotes: 8
Reputation: 738
For getting ECDiffieHellman
private key from certificate, use the following method:
CngKey cngKey = certificate.GetCngPrivateKey();
(Note: The extension method certificate.GetECDsaPrivateKey()
, natively supported in .NET, returns an ECDsaCng
instance; there is no extension method to return ECDiffieHellmanCng
.)cngKey
instance can be used to create either an ECDsaCng
or an ECDiffieHellmanCng
instance:
var sa = new ECDsaCng(cngKey);
var sa = new ECDiffieHellmanCng(cngKey);
Upvotes: 0