类 OpenSSL::PKey::DH

基于有限域中的离散对数的 Diffie-Hellman 密钥交换协议的实现,与 DSA 的基础相同。

Diffie-Hellman 参数的访问器方法

DH#p

Diffie-Hellman 参数的素数(一个 OpenSSL::BN)。

DH#g

Diffie-Hellman 参数的生成器(一个 OpenSSL::BN)g。

DH#pub_key

与私钥匹配的每会话公钥(一个 OpenSSL::BN)。这需要传递给 DH#compute_key

DH#priv_key

每会话私钥,一个 OpenSSL::BN

密钥交换示例

# 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

公共类方法

generate(size, generator = 2) → dh 点击切换源代码

通过生成随机参数和密钥对,从头开始创建一个新的 DH 实例。

另请参见 OpenSSL::PKey.generate_parametersOpenSSL::PKey.generate_key

size

所需的密钥大小(以位为单位)。

generator

生成器。

# File 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 点击切换源代码
new(string) → dh
new(size [, generator]) → dh

创建 OpenSSL::PKey::DH 的新实例。

如果在没有参数的情况下调用,则会创建一个没有参数或密钥组件的空实例。使用 set_pqg 在之后手动设置参数(并可选地使用 set_key 设置私钥和公钥组件)。

如果给定一个字符串,则尝试将其解析为 DER 或 PEM 编码的参数。另请参见 OpenSSL::PKey.read,它可以解析任何类型的密钥。

DH.new(size [, generator]) 形式是 DH.generate 的别名。

string

包含 DER 或 PEM 编码密钥的字符串。

size

参见 DH.generate

generator

参见 DH.generate

示例

# 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;
}

公共实例方法

compute_key(pub_bn) → string click to toggle source

返回一个字符串,其中包含从对方公钥计算出的共享密钥。

此方法是为了向后兼容而提供的,它在内部调用 derive

参数

# File 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

DH 参数序列化为 PEM 编码。

请注意,任何现有的每会话公钥/私钥不会被编码,只有 Diffie-Hellman 参数会被编码。

PEM 编码的参数将如下所示

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

另请参见 public_to_pem(X.509 SubjectPublicKeyInfo)和 private_to_pem(PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo),用于使用私钥或公钥组件进行序列化。

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;
}
也称为别名:to_pemto_s
generate_key! → self click to toggle source

除非私钥已存在,否则会生成私钥和公钥。如果此 DH 实例是从公钥 DH 参数生成的(例如,通过编码 DH#public_key 的结果),则需要先调用此方法才能生成每会话密钥,然后再执行实际的密钥交换。

在 3.0 版本中已弃用。此方法与 OpenSSL 3.0.0 或更高版本不兼容。

另请参见 OpenSSL::PKey.generate_key

示例

# 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 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) 点击以切换源代码
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 点击以切换源代码

将密钥的所有参数存储到哈希中,不安全:私有信息可能会泄露!不要使用 :-)) (由您决定)

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 点击以切换源代码

验证与该实例关联的 Diffie-Hellman 参数。它检查是否使用了安全素数和合适的生成器。如果不是,则返回 false

另请参见手册页 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 点击以切换源代码

指示此 DH 实例是否具有与之关联的私钥。可以使用 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 点击以切换源代码

指示此 DH 实例是否具有与之关联的公钥。可以使用 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 点击以切换源代码

返回一个新的 DH 实例,该实例只包含 DH 参数。

与方法名称相反,返回的 DH 对象只包含参数,不包含公钥。

此方法是为了向后兼容而提供的。在大多数情况下,不需要调用此方法。

为了在保持参数的同时重新生成密钥对,请检查 OpenSSL::PKey.generate_key

示例

# 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 openssl/lib/openssl/pkey.rb, line 33
def public_key
  DH.new(to_der)
end
set_key(pub_key, priv_key) → self

DH 实例设置 pub_keypriv_keypriv_key 可以是 nil

set_pqg(p, q, g) → self

pqg 设置为 DH 实例。

to_der → aString 点击以切换源代码

DH 参数序列化为 DER 编码

请注意,任何现有的每会话公钥/私钥不会被编码,只有 Diffie-Hellman 参数会被编码。

另请参见 public_to_der (X.509 SubjectPublicKeyInfo) 和 private_to_der (PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo),用于使用私钥或公钥组件进行序列化。

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

DH 参数序列化为 PEM 编码。

请注意,任何现有的每会话公钥/私钥不会被编码,只有 Diffie-Hellman 参数会被编码。

PEM 编码的参数将如下所示

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

另请参见 public_to_pem(X.509 SubjectPublicKeyInfo)和 private_to_pem(PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo),用于使用私钥或公钥组件进行序列化。

别名:export
to_s → aString

DH 参数序列化为 PEM 编码。

请注意,任何现有的每会话公钥/私钥不会被编码,只有 Diffie-Hellman 参数会被编码。

PEM 编码的参数将如下所示

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

另请参见 public_to_pem(X.509 SubjectPublicKeyInfo)和 private_to_pem(PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo),用于使用私钥或公钥组件进行序列化。

别名:export