Share via


Elliptic Curve Diffie-Hellman

The second elliptic curve algorithm added to Orcas is elliptic curve Diffie-Hellman, as the ECDiffieHellmanCng class.

This is the first time Diffie-Hellman is available as part of the .NET Framework, so lets take a quick look at what it is and what it does.  Diffie-Hellman is one of the oldest asymmetric algorithms, however unlike the other asymmetric algorithms in the framework today, it does not perform encryption or digital signatures.  Instead it allows two parties to exchange private key material even if they only can communicate through a completely public channel.  (In Network Security: Private Communication in a Public World , an amusing example is given where Diffie-Hellman is performed by two parties taking out ads in the local newspaper).

Key exchange is somewhat of a misleading term, since it implies that one party to the communication generates a key and via the communication protocol lets the other party know what that key is.  Instead what really happens is that Diffie-Hellman allows both parties to calculate the same secret value, which is referred to as the secret agreement in the managed Diffie-Hellman classes.  This secret agreement can then be used for any number of purposes, including being used as a symmetric key.

Instead of exposing the secret agreement directly however, the ECDiffieHellmanCng class does some post-processing on the agreement before letting the value out.  We refer to this post processing as the key derivation function; you can select which KDF you want to use and set its parameters via a set of properties on the instance of the Diffie-Hellman object:

Key Derivation Function Properties Meaning
Hash HashAlgorithm Hash algorithm to process the secret agreement with
SecretPrepend Optional byte array to prepend to the secret agreement before hashing it
SecretAppend Optional byte array to append to the secret agreement before hashing it
Hmac HashAlgortihm Hash algorithm to process the secret agreement with (using the HMAC version of the algorithm).
HmacKey Key used for the HMAC operation
SecretPrepend Optional byte array to prepend to the secret agreement before hashing it
SecretAppend Optional byte array to append to the secret agreement before hashing it
Tls Label TLS PRF Label
Seed TLS PRF Seed

The result of passing the secret agreement through the key derivation function is a byte array that may be used as key material for your application.  The number of bytes of key material generated is dependent on the key derivation function, for instance SHA-256 will generate 256 bits of key material, while SHA-512 will generate 512 bits of key material.

The basic flow of an elliptic curve Diffie-Hellman key exchange is:

  1. Alice and Bob create a key pair to use for the Diffie-Hellman key exchange operation
  2. Alice and Bob configure the KDF using agreed upon parameters
  3. Alice sends Bob her public key
  4. Bob sends Alice his public key
  5. Using each other's public keys, the secret agreement is generated, and the KDF is applied to the secret agreement generating key material.

In code, this looks basically as you would expect:

ECDiffieHellmanCng alice = new ECDiffieHellmanCng();

alice.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;

alice.HashAlgorithm = CngAlgorithm.Sha256;

 

ECDiffieHellmanCng bob = new ECDiffieHellmanCng();

bob.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;

bob.HashAlgorithm = CngAlgorithm.Sha256;

 

byte[] bobKey = bob.DeriveKeyMaterial(alice.PublicKey);

byte[] aliceKey = alice.DeriveKeyMaterial(bob.PublicKey);

After running this code, aliceKey and bobKey are both 32 bytes long and match each other.  Now, Alice could use this as a symmetric key:

AesCryptoServiceProvider aes = new AesCryptoServiceProvider();

aes.Key = aliceKey;

Obviously, this example is simplified as both Alice and Bob are unlikely to be running in the same process.  ECDiffieHellmanPublicKey, which is the class returned by the PublicKey property is Serializable, so that it may be sent across any remoting channel.  It also can be manually converted to and from a byte array and XML, allowing for manual serialization in advanced use cases.

One thing to note about Diffie-Hellman is that it only guarantees that both parties are generating a secret that nobody else knows about.  It does not let either party know the identity of the other.  For instance, in the above code, Alice cannot be sure that the other person in the conversation is Bob; there could potentially be a man-in-the-middle attack here.

In order to solve that, Alice or Bob could use a well-known public key that is distributed by PKI (you can pass a CngKey to the DeriveKeyMaterial API in this case).  You could also use HMAC as the KDF and a key that you know only Alice and Bob share to solve this problem.

Comments

  • Anonymous
    January 23, 2007
    The comment has been removed

  • Anonymous
    January 24, 2007
    Hi Pieter, Thanks for your feedback!  Let me try to address some of your comments: In this post I was talking about the ECDiffieHellmanCng class, which is roughly the equivilent to RSACryptoServiceProvider in the RSA hierarchy.  There is also an ECDiffieHellman base class which is the analog of RSA - namely it defines only the basic operations, and you can inherit new implementation classes if you want.  The base class simply has to be able to export its public key, and be able to generate a byte array given another party's public key. You guessed correctly that the enum for the KDF is based around CNG.  There's different structures we need to pass through depending on the KDF selected, and therefore we couldn't make it extensible like the other pseudo-enum string extensibility points.  However, we do also provide a way around this.  In addition to DeriveKeyMaterial, ECDSACNG provides a method DeriveSecretAgreement which returns back the CNG handle to the secret agreement.  If CNG v2 allows you to use a new KDF that we haven't yet wrapped or lets you get at the raw secret agreement directly, you can then P/Invoke with that handle to access the data. By exposing the secret agreement handle, we let you do just the last part of the P/Invokes rather than have to throw away the whole class and do all of them, which tends to be the case with our RSA class. Not being able to get the raw secret agreement is enforced at the CNG layer, and not at the managed wrappers.  When I was talking to the CNG guys during the design of this class, it turns out that this was by request from some certifications they were going through (although I don't recall all the details at this time). If, when you're playing around with these classes, you find that you do have some more feedback about the design, please share it here with me.  We're always glad to revisit our APIs before we  ship if there's something that we've msised. -Shawn

  • Anonymous
    January 25, 2007
    Shawn, thanks for the answer. The fact that these decisions were made because of certification requirements explains a lot about the design of CNG (and probably the CryptoAPI too). I still don't see why someone wouldn't allow access to the calculated bits (while at the same moment allowing access to the underlying key), but at least it's outside of Microsoft's control.

  • Anonymous
    July 19, 2010
    The class ECDiffieHellmanCng has properties called "HmacKey" and "UseSecretAgreementAsHmacKey". The documentation says that if UseSecretAgreementAsHmacKey is true, HmacKey property does not apply. I would like to know however, if UseSecretAgreementAsHmacKey is set to true, what is used as the actual HMAC input? I don't see any property for this and think it's a serious flaw in the API.

  • Anonymous
    July 19, 2010
    If UseSecretAgreementAsHmacKey is true, then the secret agreement that is generated from the ECDiffie-Hellman algorithm is used as the HMAC key.  There is no way for you to supply your own, because you're using a value generated by the cryptographic operation.  If you do want to supply your own HMAC key, then UseSecretAgreementAsHmacKey will be false. -Shawn

  • Anonymous
    May 03, 2011
    It is clear (from the not very clear documentation) that if UseSecretAgreementAsHmacKey is true you use the generated raw secret as HMAC "key".  What is completely unclear is what the "argument" is.  IMO, it SHOULD have used some of the public inputs.

  • Anonymous
    May 03, 2011
    It is clear (from the not very clear documentation) that if UseSecretAgreementAsHmacKey is true you use the generated raw secret as HMAC "key".  What is completely unclear is what the "argument" is.  IMO, it SHOULD have used some of the public inputs.

  • Anonymous
    September 02, 2011
    Shawn, A Rundgren, I didn't notice I had actually got more replies to this recently. I did some research earlier on my own and found out that if UseSecretAgreementAsHmacKey is true, then the secret agreement is used BOTH as the HMAC key AND also as the HMAC message input. That makes this API completely unusable and, I'm sorry to say, stupid. What I want to do is to generate HMAC(key, message) data, where key is the secret agreement and message is some other public input. This kind of functionality is for example required for the TLSv1.2 master secret generation, because the ECDiffieHellmanCng Tls KeyDerivationFunction can only be used with TLSv1.0 and TLSv1.1. According to my research that is impossible, which makes this one of the most disappointing APIs I've seen in a while. If I'm wrong I probably owe an apology to people related, but so far I don't have any data to support that. I solved this problem by writing ECDiffie-Hellman with C# from scratch without using ECDiffieHellmanCng at all. Needless to say, I'm not happy with this solution.