类 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
公共类方法
通过生成随机参数和密钥对,从头开始创建一个新的 DH
实例。
另请参见 OpenSSL::PKey.generate_parameters
和 OpenSSL::PKey.generate_key
。
size
-
所需的密钥大小(以位为单位)。
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
创建一个新的 OpenSSL::PKey::DH
实例。
如果在没有参数的情况下调用,则会创建一个没有参数或密钥组件的空实例。使用 set_pqg
在之后手动设置参数(并可选地使用 set_key
设置私钥和公钥组件)。
如果给定一个 String
,则尝试将其解析为 DER 或 PEM 编码的参数。另请参见 OpenSSL::PKey.read
,它可以解析任何类型的密钥。
DH.new
(size [, generator]) 形式是 DH.generate
的别名。
string
-
包含 DER 或 PEM 编码密钥的
String
。 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; }
公共实例方法
返回一个 String
,其中包含从对方公钥计算出的共享密钥。
此方法是为了向后兼容而提供的,它在内部调用 derive
。
参数¶ ↑
-
pub_bn 是一个
OpenSSL::BN
,不是DH#public_key
返回的DH
实例,因为该实例只包含DH
参数。
# 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
将 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; }
生成私钥和公钥,除非私钥已经存在。如果此 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 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
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; }
将密钥的所有参数存储到哈希中,不安全:私有信息可能会泄露!不要使用 :-))(由您决定)
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; }
验证与该实例关联的 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; } }
指示此 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 }
指示此 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; }
返回一个新的 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 ext/openssl/lib/openssl/pkey.rb, line 33 def public_key DH.new(to_der) end
为 DH
实例设置 pub_key 和 priv_key。priv_key 可以为 nil
。
为 DH
实例设置 p、q、g。
将 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; }
将 DH
参数序列化为 PEM 编码。
请注意,任何现有的每个会话公钥/私钥不会被编码,只有 Diffie-Hellman 参数会被编码。
PEM 编码的参数将如下所示
-----BEGIN DH PARAMETERS----- [...] -----END DH PARAMETERS-----
另请参见 public_to_pem
(X.509 SubjectPublicKeyInfo)和 private_to_pem
(PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo),用于使用私钥或公钥组件进行序列化。
将 DH
参数序列化为 PEM 编码。
请注意,任何现有的每个会话公钥/私钥不会被编码,只有 Diffie-Hellman 参数会被编码。
PEM 编码的参数将如下所示
-----BEGIN DH PARAMETERS----- [...] -----END DH PARAMETERS-----
另请参见 public_to_pem
(X.509 SubjectPublicKeyInfo)和 private_to_pem
(PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo),用于使用私钥或公钥组件进行序列化。