Raghu
Raghu

Reputation: 3069

How to properly prepare for ECC encryption using .net framework 4.7?

In ECC encryption process, I (as a sender) am assuming that I would do following:

  1. Use an elliptical curve (identified as say, NIST P-256) to generate ephemeral (temporary) public and private key pair
  2. Get hold of public key of the receiver (i.e. the other party) some how
  3. Use a scheme (say Elliptic Curve Diffie-Hellman, also known as ECDH) to derive a shared secret using other party's public key and ephemeral private key from step 1 above
  4. Use the shared secret key to derive a symmetric key using a Key Derivation Function (KDF) (say NIST Single-step KDF as documented in http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf)
  5. Finally use this symmetric key to encrypt the message by using AES.

Q1: Are there any issues with above procedure?

In .net framework 4.7, here is my first stab at it:

        var curve = ECCurve.NamedCurves.nistP256;
        var ecdhSender = ECDiffieHellman.Create(curve);

        X509Certificate2 otherPartyPublicCert = null;   //TODO: Get some how from other party and populate this variable
        byte[] otherPartyPublicKey = otherPartyPublicCert.GetPublicKey();
        ECDiffieHellmanPublicKey otherPartyECDHPublicKey = ECDiffieHellmanCngPublicKey.FromByteArray(otherPartyPublicKey, CngKeyBlobFormat.GenericPublicBlob);

        //The DeriveKeyMaterial seem to generate secret agreement, generate key and throw away the secrete agreement
        var symmetricKey = ecdhSender.DeriveKeyMaterial(otherPartyECDHPublicKey);
        // Or
        //The DeriveKeyFromHash seem to generate same key as above because SHA256 was probably implicit in above call
        var symmetricKey2 = ecdhSender.DeriveKeyFromHash(otherPartyECDHPublicKey, HashAlgorithmName.SHA256);

        //TODO: Perform encryption with above key (either symmetricKey or symmetricKey2, depending on which call we choose) using AES etc.

Q2: Does above code flow seem correct (including my comments in the code)?

Q3: Is there a difference in using either DeriveKeyMaterial or DeriveKeyFromHash? (Note: When I compared the symmetricKey and symmetricKey2, they are identical)

Q4: To properly implement NIST Single-step KDF (which calls for kdf(Z, OtherInput) where Z is the secret agreement, OtherInput consists of keydatalen and OtherInfo), should be using DeriveKeyFromHash method that has secretPrepend and secretAppend bytes signature but only populate secreteAppend bytes?

Q5: If answer is yes to Q4 and if OtherInfo includes say info "AlgorithmIDStuff", "PartyUInfoStuff", "PartyVInfoStuff" (and if I choose to implement The Concatenation Format), should I use following values following byte sequence (i.e. bit string) (Z byte values used by DeriveKeyFromHash method) in secretAppend argument:

Upvotes: 0

Views: 1888

Answers (1)

bartonjs
bartonjs

Reputation: 33088

As you know, since you numbered them, there are multiple questions here. Ideally there's one question per question, but I agree that they're related, so I'll try to answer them in one shot.

Q1: Are there any issues with above procedure?

What you've described is effectively ECIES (Elliptic Curve Integrated Encryption Scheme) (http://www.secg.org/sec1-v2.pdf), though you're missing computing a MAC key. You may want to just follow ECIES if you can't use an authenticated encryption mode.

You're also using a slightly different KDF if you're doing SP-800-56A, but that's a fine KDF.

Q2: Does above code flow seem correct (including my comments in the code)?

The flow is fine, some of the specific code isn't correct, though.

byte[] otherPartyPublicKey = otherPartyPublicCert.GetPublicKey();
ECDiffieHellmanPublicKey otherPartyECDHPublicKey =
    ECDiffieHellmanCngPublicKey.FromByteArray(
        otherPartyPublicKey,
        CngKeyBlobFormat.GenericPublicBlob);

"GenericPublicBlob" isn't "any generic public blob", but rather a kind of blob from CNG. The GetPublicKey() method returns the public key bytes, which is an algorithm-specific data blob. For ECC keys it is the encoded public key point Q.

Unless the sender of the key hates you, it will be an array which is odd in length and starts with 0x04. If that's so, you can import the public key via .NET 4.7 API:

private static ECDiffieHellmanPublicKey GetECDHPublicKey(X509Certificate2 cert)
{
    byte[] pubKey = cert.GetPublicKey();

    if (pubKey.Length % 2 == 1 && pubKey[0] == 0x04)
    {
        byte[] qx = new byte[pubKey.Length / 2];
        byte[] qy = new byte[qx.Length];
        Buffer.BlockCopy(pubKey, 1, qx, 0, qx.Length);
        Buffer.BlockCopy(pubKey, 1 + qx.Length, qy, 0, qy.Length);

        ECParameters ecParameters = new ECParameters
        {
            Curve = ECCurve.NamedCurves.nistP256,
            Q =
                    {
                        X = qx,
                        Y = qy,
                    }
        };

        using (var otherEcdh = ECDiffieHellman.Create(ecParameters))
        {
            return otherEcdh.PublicKey;
        }
    }

    throw new NotSupportedException();
}            

If you don't know ahead of time what curve to expect you can do it slightly differently:

using (ECDsa ecdsa = cert.GetECDsaPublicKey())
using (ECDiffieHellman ecdh = ECDiffieHellman.Create(ecdsa.ExportParameters(false))
{
    // This one takes care of the curve.
    return ecdh.PublicKey;
}

And of course, you'd then make your ephemeral key by something like ECDiffieHellman.Create(otherPublic.ExportParameters().Curve)

Q3: Is there a difference in using either DeriveKeyMaterial or DeriveKeyFromHash? (Note: When I compared the symmetricKey and symmetricKey2, they are identical)

DeriveKeyMaterial comes from .NET 3.5 and uses a bunch of properties to control it. By default it does Hash derivation with SHA-256.

DeriveKeyFromHash was added in .NET 4.6.2 or thereabouts to make it more clear what was being done and what properties (now parameters) were involved in the operation.

I think that no one should use DeriveKeyMaterial ever again (instead, use the new methods), but that's just me.

Q4: To properly implement NIST Single-step KDF (which calls for kdf(Z, OtherInput) where Z is the secret agreement, OtherInput consists of keydatalen and OtherInfo), should be using DeriveKeyFromHash method that has secretPrepend and secretAppend bytes signature but only populate secreteAppend bytes?

No. OtherInfo goes into secretAppend, but a big-endian 32-bit counter goes into secretPrepend (see 5.8.1.1, Process, steps 3, 4, 5.1, 5.2).

Q5: If answer is yes to Q4 and if OtherInfo includes say info "AlgorithmIDStuff", "PartyUInfoStuff", "PartyVInfoStuff" (and if I choose to implement The Concatenation Format), should I use following values following byte sequence (i.e. bit string) (Z byte values used by DeriveKeyFromHash method) in secretAppend argument:

No. "AlgorithmIDStuff" doesn't meet the definition of the AlgorithmID value (section 5.8.1.2); and similar for the PartyU/PartyV info.

Upvotes: 1

Related Questions