# class OpenSSL::PKey::DH

An implementation of the Diffie-Hellman key exchange protocol based on discrete logarithms in finite fields, the same basis that `DSA` is built on.

### Accessor methods for the Diffie-Hellman parameters¶ ↑

`DH#p`

  The prime (an `OpenSSL::BN`) of the Diffie-Hellman parameters.

DH#g

  The generator (an `OpenSSL::BN`) g of the Diffie-Hellman parameters.

DH#pub_key

  The per-session public key (an `OpenSSL::BN`) matching the private key. This needs to be passed to `DH#compute_key`.

DH#priv_key

  The per-session private key, an `OpenSSL::BN`.

### Example of a key exchange¶ ↑

    # you may send the parameters (der) and own public key (pub1) publicly
    # to the participating party
    dh1 = OpenSSL::PKey::DH.new(2048)
    der = dh1.to_der
    pub1 = dh1.pub_key

    # the other party generates its per-session key pair
    dhparams = OpenSSL::PKey::DH.new(der)
    dh2 = OpenSSL::PKey.generate_key(dhparams)
    pub2 = dh2.pub_key

    symm_key1 = dh1.compute_key(pub2)
    symm_key2 = dh2.compute_key(pub1)
    puts symm_key1 == symm_key2 # => true

### Public Class Methods

generate(size, generator = 2) → dh click to toggle source

Creates a new `DH` instance from scratch by generating random parameters and a key pair.

See also `OpenSSL::PKey.generate_parameters` and `OpenSSL::PKey.generate_key`.

`size`

  The desired key size in bits.

`generator`

  The generator.

    # File ext/openssl/lib/openssl/pkey.rb, line 118
    def generate(size, generator = 2, &blk)
      dhparams = OpenSSL::PKey.generate_parameters("DH", {
        "dh_paramgen_prime_len" => size,
        "dh_paramgen_generator" => generator,
      }, &blk)
      OpenSSL::PKey.generate_key(dhparams)
    end

new → dh click to toggle source
new(string) → dh
new(size [, generator]) → dh

Creates a new instance of `OpenSSL::PKey::DH`.

If called without arguments, an empty instance without any parameter or key components is created. Use `set_pqg` to manually set the parameters afterwards (and optionally `set_key` to set private and public key components).

If a `String` is given, tries to parse it as a DER- or PEM- encoded parameters. See also `OpenSSL::PKey.read` which can parse keys of any kinds.

The `DH.new`(size [, generator]) form is an alias of `DH.generate`.

`string`

  A `String` that contains the DER or PEM encoded key.

`size`

  See `DH.generate`.

`generator`

  See `DH.generate`.

Examples:

    # Creating an instance from scratch
    # Note that this is deprecated and will not work on OpenSSL 3.0 or later.
    dh = OpenSSL::PKey::DH.new
    dh.set_pqg(bn_p, nil, bn_g)

    # Generating a parameters and a key pair
    dh = OpenSSL::PKey::DH.new(2048) # An alias of OpenSSL::PKey::DH.generate(2048)

    # Reading DH parameters
    dh_params = OpenSSL::PKey::DH.new(File.read('parameters.pem')) # loads parameters only
    dh = OpenSSL::PKey.generate_key(dh_params) # generates a key pair

    static VALUE
    ossl_dh_initialize(int argc, VALUE *argv, VALUE self)
    {
        EVP_PKEY *pkey;
        int type;
        DH *dh;
        BIO *in = NULL;
        VALUE arg;

        TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
        if (pkey)
            rb_raise(rb_eTypeError, "pkey already initialized");

        /* The DH.new(size, generator) form is handled by lib/openssl/pkey.rb */
        if (rb_scan_args(argc, argv, "01", &arg) == 0) {
            dh = DH_new();
            if (!dh)
                ossl_raise(eDHError, "DH_new");
            goto legacy;
        }

        arg = ossl_to_der_if_possible(arg);
        in = ossl_obj2bio(&arg);

        /*
         * On OpenSSL <= 1.1.1 and current versions of LibreSSL, the generic
         * routine does not support DER-encoded parameters
         */
        dh = d2i_DHparams_bio(in, NULL);
        if (dh)
            goto legacy;
        OSSL_BIO_reset(in);

        pkey = ossl_pkey_read_generic(in, Qnil);
        BIO_free(in);
        if (!pkey)
            ossl_raise(eDHError, "could not parse pkey");

        type = EVP_PKEY_base_id(pkey);
        if (type != EVP_PKEY_DH) {
            EVP_PKEY_free(pkey);
            rb_raise(eDHError, "incorrect pkey type: %s", OBJ_nid2sn(type));
        }
        RTYPEDDATA_DATA(self) = pkey;
        return self;

      legacy:
        BIO_free(in);
        pkey = EVP_PKEY_new();
        if (!pkey || EVP_PKEY_assign_DH(pkey, dh) != 1) {
            EVP_PKEY_free(pkey);
            DH_free(dh);
            ossl_raise(eDHError, "EVP_PKEY_assign_DH");
        }
        RTYPEDDATA_DATA(self) = pkey;
        return self;
    }

### Public Instance Methods

compute_key(pub_bn) → string click to toggle source

Returns a `String` containing a shared secret computed from the other party’s public value.

This method is provided for backwards compatibility, and calls `derive` internally.

### Parameters¶ ↑

- pub_bn is a `OpenSSL::BN`, not the `DH` instance returned by `DH#public_key` as that contains the `DH` parameters only.

    # File ext/openssl/lib/openssl/pkey.rb, line 49
    def compute_key(pub_bn)
      # FIXME: This is constructing an X.509 SubjectPublicKeyInfo and is very
      # inefficient
      obj = OpenSSL::ASN1.Sequence([
        OpenSSL::ASN1.Sequence([
          OpenSSL::ASN1.ObjectId("dhKeyAgreement"),
          OpenSSL::ASN1.Sequence([
            OpenSSL::ASN1.Integer(p),
            OpenSSL::ASN1.Integer(g),
          ]),
        ]),
        OpenSSL::ASN1.BitString(OpenSSL::ASN1.Integer(pub_bn).to_der),
      ])
      derive(OpenSSL::PKey.read(obj.to_der))
    end

export → aString click to toggle source

Serializes the `DH` parameters to a PEM-encoding.

Note that any existing per-session public/private keys will not get encoded, just the Diffie-Hellman parameters will be encoded.

PEM-encoded parameters will look like:

    -----BEGIN DH PARAMETERS-----
    [...]
    -----END DH PARAMETERS-----

See also `public_to_pem` (X.509 SubjectPublicKeyInfo) and `private_to_pem` (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) for serialization with the private or public key components.

    static VALUE
    ossl_dh_export(VALUE self)
    {
        OSSL_3_const DH *dh;
        BIO *out;
        VALUE str;

        GetDH(self, dh);
        if (!(out = BIO_new(BIO_s_mem()))) {
            ossl_raise(eDHError, NULL);
        }
        if (!PEM_write_bio_DHparams(out, dh)) {
            BIO_free(out);
            ossl_raise(eDHError, NULL);
        }
        str = ossl_membio2str(out);

        return str;
    }

Also aliased as: to_pem, to_s
generate_key! → self click to toggle source

Generates a private and public key unless a private key already exists. If this `DH` instance was generated from public DH parameters (e.g. by encoding the result of `DH#public_key`), then this method needs to be called first in order to generate the per-session keys before performing the actual key exchange.

Deprecated in version 3.0. This method is incompatible with `OpenSSL` 3.0.0 or later.

See also `OpenSSL::PKey.generate_key`.

Example:

    # DEPRECATED USAGE: This will not work on OpenSSL 3.0 or later
    dh0 = OpenSSL::PKey::DH.new(2048)
    dh = dh0.public_key # #public_key only copies the DH parameters (contrary to the name)
    dh.generate_key!
    puts dh.private? # => true
    puts dh0.pub_key == dh.pub_key #=> false

    # With OpenSSL::PKey.generate_key
    dh0 = OpenSSL::PKey::DH.new(2048)
    dh = OpenSSL::PKey.generate_key(dh0)
    puts dh0.pub_key == dh.pub_key #=> false

    # File ext/openssl/lib/openssl/pkey.rb, line 91
    def generate_key!
      if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x30000000
        raise DHError, "OpenSSL::PKey::DH is immutable on OpenSSL 3.0; " \
        "use OpenSSL::PKey.generate_key instead"
      end

      unless priv_key
        tmp = OpenSSL::PKey.generate_key(self)
        set_key(tmp.pub_key, tmp.priv_key)
      end
      self
    end

initialize_copy(p1) click to toggle source

    HAVE_EVP_PKEY_DUP
    static VALUE
    ossl_dh_initialize_copy(VALUE self, VALUE other)
    {
        EVP_PKEY *pkey;
        DH *dh, *dh_other;
        const BIGNUM *pub, *priv;

        TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
        if (pkey)
            rb_raise(rb_eTypeError, "pkey already initialized");
        GetDH(other, dh_other);

        dh = DHparams_dup(dh_other);
        if (!dh)
            ossl_raise(eDHError, "DHparams_dup");

        DH_get0_key(dh_other, &pub, &priv);
        if (pub) {
            BIGNUM *pub2 = BN_dup(pub);
            BIGNUM *priv2 = BN_dup(priv);

            if (!pub2 || (priv && !priv2)) {
                BN_clear_free(pub2);
                BN_clear_free(priv2);
                ossl_raise(eDHError, "BN_dup");
            }
            DH_set0_key(dh, pub2, priv2);
        }

        pkey = EVP_PKEY_new();
        if (!pkey || EVP_PKEY_assign_DH(pkey, dh) != 1) {
            EVP_PKEY_free(pkey);
            DH_free(dh);
            ossl_raise(eDHError, "EVP_PKEY_assign_DH");
        }
        RTYPEDDATA_DATA(self) = pkey;
        return self;
    }

params → hash click to toggle source

Stores all parameters of key to the hash INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! Don’t use :-)) (I’s up to you)

    static VALUE
    ossl_dh_get_params(VALUE self)
    {
        OSSL_3_const DH *dh;
        VALUE hash;
        const BIGNUM *p, *q, *g, *pub_key, *priv_key;

        GetDH(self, dh);
        DH_get0_pqg(dh, &p, &q, &g);
        DH_get0_key(dh, &pub_key, &priv_key);

        hash = rb_hash_new();
        rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p));
        rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q));
        rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(g));
        rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pub_key));
        rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(priv_key));

        return hash;
    }

params_ok? → true | false click to toggle source

Validates the Diffie-Hellman parameters associated with this instance. It checks whether a safe prime and a suitable generator are used. If this is not the case, `false` is returned.

See also the man page EVP_PKEY_param_check(3).

    static VALUE
    ossl_dh_check_params(VALUE self)
    {
        int ret;
    #ifdef HAVE_EVP_PKEY_CHECK
        EVP_PKEY *pkey;
        EVP_PKEY_CTX *pctx;

        GetPKey(self, pkey);
        pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
        if (!pctx)
            ossl_raise(eDHError, "EVP_PKEY_CTX_new");
        ret = EVP_PKEY_param_check(pctx);
        EVP_PKEY_CTX_free(pctx);
    #else
        DH *dh;
        int codes;

        GetDH(self, dh);
        ret = DH_check(dh, &codes) == 1 && codes == 0;
    #endif

        if (ret == 1)
            return Qtrue;
        else {
            /* DH_check_ex() will put error entry on failure */
            ossl_clear_error();
            return Qfalse;
        }
    }

private? → true | false click to toggle source

Indicates whether this `DH` instance has a private key associated with it or not. The private key may be retrieved with DH#priv_key.

    static VALUE
    ossl_dh_is_private(VALUE self)
    {
        OSSL_3_const DH *dh;
        const BIGNUM *bn;

        GetDH(self, dh);
        DH_get0_key(dh, NULL, &bn);

    #if !defined(OPENSSL_NO_ENGINE)
        return (bn || DH_get0_engine((DH *)dh)) ? Qtrue : Qfalse;
    #else
        return bn ? Qtrue : Qfalse;
    #endif
    }

public? → true | false click to toggle source

Indicates whether this `DH` instance has a public key associated with it or not. The public key may be retrieved with DH#pub_key.

    static VALUE
    ossl_dh_is_public(VALUE self)
    {
        OSSL_3_const DH *dh;
        const BIGNUM *bn;

        GetDH(self, dh);
        DH_get0_key(dh, &bn, NULL);

        return bn ? Qtrue : Qfalse;
    }

public_key → dhnew click to toggle source

Returns a new `DH` instance that carries just the DH parameters.

Contrary to the method name, the returned `DH` object contains only parameters and not the public key.

This method is provided for backwards compatibility. In most cases, there is no need to call this method.

For the purpose of re-generating the key pair while keeping the parameters, check `OpenSSL::PKey.generate_key`.

Example:

    # OpenSSL::PKey::DH.generate by default generates a random key pair
    dh1 = OpenSSL::PKey::DH.generate(2048)
    p dh1.priv_key #=> #<OpenSSL::BN 1288347...>
    dhcopy = dh1.public_key
    p dhcopy.priv_key #=> nil

    # File ext/openssl/lib/openssl/pkey.rb, line 33
    def public_key
      DH.new(to_der)
    end

set_key(pub_key, priv_key) → self

Sets pub_key and priv_key for the `DH` instance. priv_key may be `nil`.

set_pqg(p, q, g) → self

Sets p, q, g to the `DH` instance.

to_der → aString click to toggle source

Serializes the `DH` parameters to a DER-encoding

Note that any existing per-session public/private keys will not get encoded, just the Diffie-Hellman parameters will be encoded.

See also `public_to_der` (X.509 SubjectPublicKeyInfo) and `private_to_der` (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) for serialization with the private or public key components.

    static VALUE
    ossl_dh_to_der(VALUE self)
    {
        OSSL_3_const DH *dh;
        unsigned char *p;
        long len;
        VALUE str;

        GetDH(self, dh);
        if((len = i2d_DHparams(dh, NULL)) <= 0)
            ossl_raise(eDHError, NULL);
        str = rb_str_new(0, len);
        p = (unsigned char *)RSTRING_PTR(str);
        if(i2d_DHparams(dh, &p) < 0)
            ossl_raise(eDHError, NULL);
        ossl_str_adjust(str, p);

        return str;
    }

to_pem → aString

Serializes the `DH` parameters to a PEM-encoding.

Note that any existing per-session public/private keys will not get encoded, just the Diffie-Hellman parameters will be encoded.

PEM-encoded parameters will look like:

    -----BEGIN DH PARAMETERS-----
    [...]
    -----END DH PARAMETERS-----

See also `public_to_pem` (X.509 SubjectPublicKeyInfo) and `private_to_pem` (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) for serialization with the private or public key components.

Alias for: export
to_s → aString

Serializes the `DH` parameters to a PEM-encoding.

Note that any existing per-session public/private keys will not get encoded, just the Diffie-Hellman parameters will be encoded.

PEM-encoded parameters will look like:

    -----BEGIN DH PARAMETERS-----
    [...]
    -----END DH PARAMETERS-----

See also `public_to_pem` (X.509 SubjectPublicKeyInfo) and `private_to_pem` (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) for serialization with the private or public key components.

Alias for: export