Difference between revisions of "Diffie Hellman"

From OpenSSLWiki
Jump to: navigation, search
(Updated info on X9.42 and added reference to NIST 800-56A)
m (Added info on the Diffie-Hellman schemes used by SSL/TLS; Formatting)
Line 1: Line 1:
The Diffie-Hellman algorithm provides the capability for two communicating parties to agree a shared secret between them. This can then be used as the basis for some encryption key to be used for further communication.
+
The Diffie-Hellman algorithm provides the capability for two communicating parties to agree upon a shared secret between them. Its an agreement scheme because both parties add material used to derive the key (as opposed to transport, where one party selects the key). The shared secret can then be used as the basis for some encryption key to be used for further communication.
  
 
If Alice and Bob wish to communicate with each other, they first agree between them a large prime number p, and a generator (or base) g (where 0 < g < p).
 
If Alice and Bob wish to communicate with each other, they first agree between them a large prime number p, and a generator (or base) g (where 0 < g < p).
Line 18: Line 18:
 
* [ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-3.asc PKCS 3] defines the basic algorithm and  data formats to be used.
 
* [ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-3.asc PKCS 3] defines the basic algorithm and  data formats to be used.
 
* ANSI X9.42 is a later standard than PKCS 3 and provides further guidance on its use (note OpenSSL does not support ANSI X9.42 in the released versions - support is available in the as yet unreleased 1.0.2 and 1.1.0)
 
* ANSI X9.42 is a later standard than PKCS 3 and provides further guidance on its use (note OpenSSL does not support ANSI X9.42 in the released versions - support is available in the as yet unreleased 1.0.2 and 1.1.0)
* [http://www.ietf.org/rfc/rfc2631.txt RFC 2631] summarises the key points of ANSI X9.42
+
* [http://www.ietf.org/rfc/rfc2631.txt RFC 2631] summarizes the key points of ANSI X9.42
 
* [http://www.ietf.org/rfc/rfc5114 RFC 5114] defines 3 standard sets of parameters for use with Diffie-Hellman (OpenSSL will have built-in support for these parameters from OpenSSL 1.0.2 - not yet released)  
 
* [http://www.ietf.org/rfc/rfc5114 RFC 5114] defines 3 standard sets of parameters for use with Diffie-Hellman (OpenSSL will have built-in support for these parameters from OpenSSL 1.0.2 - not yet released)  
 
* [http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf NIST SP 800-56A] is a NIST publication that provides recommendations on the implementation of X9.42
 
* [http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf NIST SP 800-56A] is a NIST publication that provides recommendations on the implementation of X9.42
 +
 +
==Diffie-Hellman in SSL/TLS==
 +
 +
There are three versions of Diffie-Hellman used in SSL/TLS.
 +
 +
* Anonymous Diffie-Hellman
 +
* Fixed Diffie-Hellman
 +
* Ephemeral Diffie-Hellman
 +
 +
'''Anonymous Diffie-Hellman''' uses Diffie-Hellman, but without authentication. Because the keys used in the exchange are not authenticated, the protocol is susceptible to Man-in-the-Middle attacks.
 +
 +
You should not use Anonymous Diffie-Hellman. You can prohibit its use in your code by using <tt>"!ADH"</tt> in your call to <tt>SSL_set_cipher_list</tt>.
 +
 +
'''Fixed Diffie-Hellman''' embeds the server's public parameter in the certificate, and the CA then signs the certificate. That is, the certificate contains the Diffie-Hellman public-key parameters, and those parameters never change.
 +
 +
'''Ephemeral Diffie-Hellman''' uses temporary, public keys. Each instance or run of the protocol uses a different public key. The authenticity of the server's temporary key can be verified by checking the signature on the key. Because the public keys are temporary, a compromise of the server's long term signing key ''does not'' jeopardize the privacy of past sessions. This is known as ''Perfect Forward Secrecy (PFS)''.
 +
 +
You should always use Ephemeral Diffie-Hellman because it provides PFS. You can specify ephemeral methods by providing <tt>"kEECDH:kEDH"</tt> in your call to <tt>SSL_set_cipher_list</tt>.
  
 
==Working with Parameters and Generating Keys==
 
==Working with Parameters and Generating Keys==
Line 26: Line 44:
 
The first step with the Diffie-Hellman algorithm is to ensure that both parties are using the same set of parameters (i.e. the same values for p and g). Since parameter generation can be an expensive process this is normally done once in advance and then the same set of parameters are used over many key exchanges. A new set of parameters can be generated by OpenSSL, or alternatively there is support for built-in standard sets of parameters.
 
The first step with the Diffie-Hellman algorithm is to ensure that both parties are using the same set of parameters (i.e. the same values for p and g). Since parameter generation can be an expensive process this is normally done once in advance and then the same set of parameters are used over many key exchanges. A new set of parameters can be generated by OpenSSL, or alternatively there is support for built-in standard sets of parameters.
  
<pre>
+
<pre>/* Use built-in parameters */
/* Use built-in parameters */
+
if(NULL == (params = EVP_PKEY_new())) handleErrors();
if(NULL == (params = EVP_PKEY_new())) handleErrors();
+
if(1 != EVP_PKEY_set1_DH(params,DH_get_2048_256())) handleErrors();
if(1 != EVP_PKEY_set1_DH(params,DH_get_2048_256())) handleErrors();
 
  
/* Create context for the key generation */
+
/* Create context for the key generation */
if(!(kctx = EVP_PKEY_CTX_new(params, NULL))) handleErrors();
+
if(!(kctx = EVP_PKEY_CTX_new(params, NULL))) handleErrors();
  
/* Generate a new key */
+
/* Generate a new key */
if(1 != EVP_PKEY_keygen_init(kctx)) handleErrors();
+
if(1 != EVP_PKEY_keygen_init(kctx)) handleErrors();
if(1 != EVP_PKEY_keygen(kctx, &dhkey)) handleErrors();
+
if(1 != EVP_PKEY_keygen(kctx, &dhkey)) handleErrors();</pre>
</pre>
 
  
 
To generate your own parameters refer to [[EVP Key and Parameter Generation]].
 
To generate your own parameters refer to [[EVP Key and Parameter Generation]].
Line 51: Line 67:
 
Users of the OpenSSL library are expected to normally use the EVP method for working with Diffie Hellman as described above and on the [[EVP Key Agreement]] page. The EVP api is implemented by a lower level Diffie Hellman API. In some circumstances, expert users may need to use the low level api. '''This is not recommended for most users'''. However, if you need to use this then an example of use is shown below. The manual page for the low level API is available here: [[Manual:dh(3)]]
 
Users of the OpenSSL library are expected to normally use the EVP method for working with Diffie Hellman as described above and on the [[EVP Key Agreement]] page. The EVP api is implemented by a lower level Diffie Hellman API. In some circumstances, expert users may need to use the low level api. '''This is not recommended for most users'''. However, if you need to use this then an example of use is shown below. The manual page for the low level API is available here: [[Manual:dh(3)]]
  
<pre>
+
<pre>DH *privkey;
DH *privkey;
+
int codes;
int codes;
+
int secret_size;
int secret_size;
 
  
/* Generate the parameters to be used */
+
/* Generate the parameters to be used */
if(NULL == (privkey = DH_new())) handleErrors();
+
if(NULL == (privkey = DH_new())) handleErrors();
if(1 != DH_generate_parameters_ex(privkey, 1024, DH_GENERATOR_2, NULL)) handleErrors();
+
if(1 != DH_generate_parameters_ex(privkey, 1024, DH_GENERATOR_2, NULL)) handleErrors();
  
if(1 != DH_check(privkey, &codes)) handleErrors();
+
if(1 != DH_check(privkey, &codes)) handleErrors();
if(codes != 0)
+
if(codes != 0)
{
+
{
/* Problems have been found with the generated parameters */
+
    /* Problems have been found with the generated parameters */
/* Handle these here - we'll just abort for this example */
+
    /* Handle these here - we'll just abort for this example */
printf("DH_check failed\n");
+
    printf("DH_check failed\n");
abort();
+
    abort();
}
+
}
  
/* Generate the public and private key pair */
+
/* Generate the public and private key pair */
if(1 != DH_generate_key(privkey)) handleErrors();
+
if(1 != DH_generate_key(privkey)) handleErrors();
  
/* Send the public key to the peer. In this example we just write it out to a file */
+
/* Send the public key to the peer. In this example we just write it out to a file */
BIO *bp;
+
BIO *bp = NULL;
if(NULL == (bp = BIO_new_file("out.dhparams", "wb"))) handleErrors();
+
if(NULL == (bp = BIO_new_file("out.dhparams", "wb"))) handleErrors();
if(1 != i2d_DHparams_bio(bp, privkey)) handleErrors();
+
if(1 != i2d_DHparams_bio(bp, privkey)) handleErrors();
BIO_free(bp);
+
BIO_free(bp);
  
/* Receive the public key from the peer. In this example we're just hard coding a value */
+
/* Receive the public key from the peer. In this example we're just hard coding a value */
BIGNUM *pubkey = NULL;
+
BIGNUM *pubkey = NULL;
if(0 == (BN_dec2bn(&pubkey, "01234567890123456789012345678901234567890123456789"))) handleErrors();
+
if(0 == (BN_dec2bn(&pubkey, "01234567890123456789012345678901234567890123456789"))) handleErrors();
  
/* Compute the shared secret */
+
/* Compute the shared secret */
unsigned char *secret;
+
unsigned char *secret;
if(NULL == (secret = OPENSSL_malloc(sizeof(unsigned char) * (DH_size(privkey))))) handleErrors();
+
if(NULL == (secret = OPENSSL_malloc(sizeof(unsigned char) * (DH_size(privkey))))) handleErrors();
  
if(0 > (secret_size = DH_compute_key(secret, pubkey, privkey))) handleErrors();
+
if(0 > (secret_size = DH_compute_key(secret, pubkey, privkey))) handleErrors();
  
/* Do something with the shared secret */
+
/* Do something with the shared secret */
printf("The shared secret is:\n");
+
printf("The shared secret is:\n");
BIO_dump_fp(stdout, secret, DH_size(privkey));
+
BIO_dump_fp(stdout, secret, DH_size(privkey));
  
/* Clean up */
+
/* Clean up */
OPENSSL_free(secret);
+
OPENSSL_free(secret);
BN_free(pubkey);
+
BN_free(pubkey);
DH_free(privkey);
+
DH_free(privkey);
  
 
</pre>
 
</pre>

Revision as of 10:49, 13 October 2013

The Diffie-Hellman algorithm provides the capability for two communicating parties to agree upon a shared secret between them. Its an agreement scheme because both parties add material used to derive the key (as opposed to transport, where one party selects the key). The shared secret can then be used as the basis for some encryption key to be used for further communication.

If Alice and Bob wish to communicate with each other, they first agree between them a large prime number p, and a generator (or base) g (where 0 < g < p).

Alice chooses a secret integer a (her private key) and then calculates ga mod p (which is her public key). Bob chooses his private key b, and calculates his public key in the same way.

Alice and Bob then send each other their public keys. Alice now knows a and Bob's public key gb mod p. She is not able to calculate the value b from Bob's public key as this is a hard mathematical problem (known as the discrete logarithm problem). She can however calculate (gb)a mod p = gab mod p.

Bob knows b and ga, so he can calculate (ga)b mod p = gab mod p. Therefore both Alice and Bob know a shared secret gab mod p. Eve who was listening in on the communication knows p, g, Alice's public key (ga mod p) and Bob's public key (gb mod p). She is unable to calculate the shared secret from these values.

In static-static mode both Alice and Bob retain their private/public keys over multiple communications. Therefore the resulting shared secret will be the same every time. In ephemeral-static mode one party will generate a new private/public key every time, thus a new shared secret will be generated.

Diffie-Hellman Standards

There are a number of standards relevant to Diffie-Hellman key agreement. Some of the key ones are:

  • PKCS 3 defines the basic algorithm and data formats to be used.
  • ANSI X9.42 is a later standard than PKCS 3 and provides further guidance on its use (note OpenSSL does not support ANSI X9.42 in the released versions - support is available in the as yet unreleased 1.0.2 and 1.1.0)
  • RFC 2631 summarizes the key points of ANSI X9.42
  • RFC 5114 defines 3 standard sets of parameters for use with Diffie-Hellman (OpenSSL will have built-in support for these parameters from OpenSSL 1.0.2 - not yet released)
  • NIST SP 800-56A is a NIST publication that provides recommendations on the implementation of X9.42

Diffie-Hellman in SSL/TLS

There are three versions of Diffie-Hellman used in SSL/TLS.

  • Anonymous Diffie-Hellman
  • Fixed Diffie-Hellman
  • Ephemeral Diffie-Hellman

Anonymous Diffie-Hellman uses Diffie-Hellman, but without authentication. Because the keys used in the exchange are not authenticated, the protocol is susceptible to Man-in-the-Middle attacks.

You should not use Anonymous Diffie-Hellman. You can prohibit its use in your code by using "!ADH" in your call to SSL_set_cipher_list.

Fixed Diffie-Hellman embeds the server's public parameter in the certificate, and the CA then signs the certificate. That is, the certificate contains the Diffie-Hellman public-key parameters, and those parameters never change.

Ephemeral Diffie-Hellman uses temporary, public keys. Each instance or run of the protocol uses a different public key. The authenticity of the server's temporary key can be verified by checking the signature on the key. Because the public keys are temporary, a compromise of the server's long term signing key does not jeopardize the privacy of past sessions. This is known as Perfect Forward Secrecy (PFS).

You should always use Ephemeral Diffie-Hellman because it provides PFS. You can specify ephemeral methods by providing "kEECDH:kEDH" in your call to SSL_set_cipher_list.

Working with Parameters and Generating Keys

The first step with the Diffie-Hellman algorithm is to ensure that both parties are using the same set of parameters (i.e. the same values for p and g). Since parameter generation can be an expensive process this is normally done once in advance and then the same set of parameters are used over many key exchanges. A new set of parameters can be generated by OpenSSL, or alternatively there is support for built-in standard sets of parameters.

/* Use built-in parameters */
if(NULL == (params = EVP_PKEY_new())) handleErrors();
if(1 != EVP_PKEY_set1_DH(params,DH_get_2048_256())) handleErrors();

/* Create context for the key generation */
if(!(kctx = EVP_PKEY_CTX_new(params, NULL))) handleErrors();

/* Generate a new key */
if(1 != EVP_PKEY_keygen_init(kctx)) handleErrors();
if(1 != EVP_PKEY_keygen(kctx, &dhkey)) handleErrors();

To generate your own parameters refer to EVP Key and Parameter Generation.

Note: The function DH_get_2048_256 is scheduled for release in OpenSSL 1.0.2; it is not available in 1.0.1e or earlier.

Generating a Shared Secret

Having obtained a private/public key pair you need to also obtain the public key of the other communicating party. Refer to EVP Key Agreement for information on how to agree a shared secret.

Using the Low Level APIs

Users of the OpenSSL library are expected to normally use the EVP method for working with Diffie Hellman as described above and on the EVP Key Agreement page. The EVP api is implemented by a lower level Diffie Hellman API. In some circumstances, expert users may need to use the low level api. This is not recommended for most users. However, if you need to use this then an example of use is shown below. The manual page for the low level API is available here: Manual:dh(3)

DH *privkey;
int codes;
int secret_size;

/* Generate the parameters to be used */
if(NULL == (privkey = DH_new())) handleErrors();
if(1 != DH_generate_parameters_ex(privkey, 1024, DH_GENERATOR_2, NULL)) handleErrors();

if(1 != DH_check(privkey, &codes)) handleErrors();
if(codes != 0)
{
    /* Problems have been found with the generated parameters */
    /* Handle these here - we'll just abort for this example */
    printf("DH_check failed\n");
    abort();
}

/* Generate the public and private key pair */
if(1 != DH_generate_key(privkey)) handleErrors();

/* Send the public key to the peer. In this example we just write it out to a file */
BIO *bp = NULL;
if(NULL == (bp = BIO_new_file("out.dhparams", "wb"))) handleErrors();
if(1 != i2d_DHparams_bio(bp, privkey)) handleErrors();
BIO_free(bp);

/* Receive the public key from the peer. In this example we're just hard coding a value */
BIGNUM *pubkey = NULL;
if(0 == (BN_dec2bn(&pubkey, "01234567890123456789012345678901234567890123456789"))) handleErrors();

/* Compute the shared secret */
unsigned char *secret;
if(NULL == (secret = OPENSSL_malloc(sizeof(unsigned char) * (DH_size(privkey))))) handleErrors();

if(0 > (secret_size = DH_compute_key(secret, pubkey, privkey))) handleErrors();

/* Do something with the shared secret */
printf("The shared secret is:\n");
BIO_dump_fp(stdout, secret, DH_size(privkey));

/* Clean up */
OPENSSL_free(secret);
BN_free(pubkey);
DH_free(privkey);

See also