简体   繁体   中英

How can I encrypt data using a public key from ECC X509 certificate in .net framework on windows?

I am using:

  • Windows 10 (Version 1709, OS Build 17025.1000)
  • .net framework 4.7
  • VS 2017 (version: 15.3.5)

Here is what I did:

  1. 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

  2. Load the certificate from file (generated in previous step) into X509Certificate2 object

  3. Imported the PFX file into windows trust store (for testing). This is successful.

  4. Inspection of the imported certificate shows Public Key field as 'ECC (256 Bits)' and Public key parameters as 'ECDSA_P256'.
  5. Next tried to figure out how to encrypt with this certificate.

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.

Background

Asymmetric algorithms have three different purposes (that I know of)

  1. Encryption
    • RSA is the only "standard" algorithm that can do this directly.
  2. Signature
    • RSA
    • DSA
    • ECDSA
    • ElGamal Signature
  3. Key Agreement
    • Diffie-Hellman (DH)
    • ECDH
    • ElGamal encryption (the asymmetric startup phase)
    • MQV
    • ECMQV

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://tools.ietf.org/html/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

  • TLS (nee SSL)
  • CMS or S/MIME encrypted-data
  • IES (Integrated Encryption Scheme)
  • ECIES (Elliptic Curve Integrated Encryption Scheme)
  • ElGamal encryption (holistically)
  • PGP encryption

So what you probably want is ECIES.

ECIES.Net

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)

Setup

  1. Choose ANSI-X9.63-KDF with SHA-2-256 as the hash function.
  2. Choose HMAC–SHA-256–256.
  3. Choose AES–256 in CBC mode.
  4. Choose Elliptic Curve Diffie-Hellman Primitive.
  5. You already chose secp256r1.
  6. Hard-coded. Done.
  7. Point compression's annoying, choose not to use it.
  8. I'm omitting SharedInfo. That probably makes me a bad person.
  9. Not using XOR, N/A.

Encrypt

  1. Make an ephemeral key on the right curve.

     ECDiffieHellman ephem = ECDiffieHellman.Create(curve); 
  2. We decided no.

     ECParameters ephemPublicParams = ephem.ExportParameters(false); int pointLen = ephemPublicParams.QXLength; byte[] rBar = new byte[pointLen * 2 + 1]; rBar[0] = 0x04; Buffer.BlockCopy(ephemPublicParams.QX, 0, rBar, 1, pointLen); Buffer.BlockCopy(ephemPublicParams.QY, 0, rBar, 1 + pointLen, pointLen); 
  3. Can't directly do this, moving on.

  4. Can't directly do this, moving on.
  5. Since we're in control here, we'll just do 3, 4, 5, and 6 as one thing.
  6. 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 }); 
  7. 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); } 
  8. MAC it

     byte[] d; using (HMAC hmac = new HMACSHA256(mk)) { d = hmac.ComputeHash(em); } 
  9. Finish

     // Either return Tuple.Create(rBar, em, d); // Or return rBar.Concat(em).Concat(d).ToArray(); 

Decrypt

Left as an exercise to the reader.

For getting ECDiffieHellman private key from certificate, use the following method:

  • Install NuGet package Security.Cryptography (CLR Security). (The package is under MIT license .)
  • Use the following extension method to get the CngKey instance: 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 .)
  • The cngKey instance can be used to create either an ECDsaCng or an ECDiffieHellmanCng instance: var sa = new ECDsaCng(cngKey); var sa = new ECDiffieHellmanCng(cngKey); var sa = new ECDsaCng(cngKey); var sa = new ECDiffieHellmanCng(cngKey);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM