类 OpenSSL::PKey::RSA
RSA
是一种非对称公钥算法,已在 RFC 3447 中正式化。它在公钥基础设施 (PKI) 中被广泛使用,其中证书(参见 OpenSSL::X509::Certificate
)通常基于公钥/私钥 RSA
密钥对颁发。 RSA
用于各种应用领域,例如安全(对称)密钥交换,例如在建立安全的 TLS/SSL 连接时。它也用于各种数字签名方案。
常量
- NO_PADDING
- PKCS1_OAEP_PADDING
- PKCS1_PADDING
- SSLV23_PADDING
公共类方法
生成 RSA 密钥对。
另请参见 OpenSSL::PKey.generate_key
.
size
-
所需的密钥大小(以位为单位)。
exponent
-
一个奇数
Integer
,通常为 3、17 或 65537。
# File ext/openssl/lib/openssl/pkey.rb, line 343 def generate(size, exp = 0x10001, &blk) OpenSSL::PKey.generate_key("RSA", { "rsa_keygen_bits" => size, "rsa_keygen_pubexp" => exp, }, &blk) end
生成或加载 RSA 密钥对。
如果在不带参数的情况下调用,则创建一个没有设置密钥组件的新实例。它们可以通过 set_key
、set_factors
和 set_crt_params
分别设置。
如果使用 String
调用,则尝试解析为 RSA 密钥的 DER 或 PEM 编码。请注意,如果未指定password,但密钥使用密码加密,OpenSSL 会提示输入密码。另请参见 OpenSSL::PKey.read
,它可以解析任何类型的密钥。
如果使用数字调用,则生成一个新的密钥对。此形式作为 RSA.generate
的别名。
示例
OpenSSL::PKey::RSA.new 2048 OpenSSL::PKey::RSA.new File.read 'rsa.pem' OpenSSL::PKey::RSA.new File.read('rsa.pem'), 'my password'
static VALUE ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; RSA *rsa; BIO *in = NULL; VALUE arg, pass; int type; TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey); if (pkey) rb_raise(rb_eTypeError, "pkey already initialized"); /* The RSA.new(size, generator) form is handled by lib/openssl/pkey.rb */ rb_scan_args(argc, argv, "02", &arg, &pass); if (argc == 0) { rsa = RSA_new(); if (!rsa) ossl_raise(eRSAError, "RSA_new"); goto legacy; } pass = ossl_pem_passwd_value(pass); arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); /* First try RSAPublicKey format */ rsa = d2i_RSAPublicKey_bio(in, NULL); if (rsa) goto legacy; OSSL_BIO_reset(in); rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL); if (rsa) goto legacy; OSSL_BIO_reset(in); /* Use the generic routine */ pkey = ossl_pkey_read_generic(in, pass); BIO_free(in); if (!pkey) ossl_raise(eRSAError, "Neither PUB key nor PRIV key"); type = EVP_PKEY_base_id(pkey); if (type != EVP_PKEY_RSA) { EVP_PKEY_free(pkey); rb_raise(eRSAError, "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_RSA(pkey, rsa) != 1) { EVP_PKEY_free(pkey); RSA_free(rsa); ossl_raise(eRSAError, "EVP_PKEY_assign_RSA"); } RTYPEDDATA_DATA(self) = pkey; return self; }
公共实例方法
将私钥或公钥序列化为 PEM 编码。
- 当密钥仅包含公钥组件时
-
将其序列化为 X.509 SubjectPublicKeyInfo。参数cipher 和password 被忽略。
PEM 编码的密钥将如下所示
-----BEGIN PUBLIC KEY----- [...] -----END PUBLIC KEY-----
考虑使用
public_to_pem
代替。这将密钥序列化为 X.509 SubjectPublicKeyInfo,无论密钥是公钥还是私钥。 - 当密钥包含私钥组件,并且没有给出参数时
-
将其序列化为 PKCS #1 RSAPrivateKey。
PEM 编码的密钥将如下所示
-----BEGIN RSA PRIVATE KEY----- [...] -----END RSA PRIVATE KEY-----
- 当密钥包含私钥组件,并且给出cipher 和password 时
-
将其序列化为 PKCS #1 RSAPrivateKey 并使用 OpenSSL 的传统 PEM 加密格式对其进行加密。cipher 必须是
OpenSSL::Cipher.new
理解的密码名称,或OpenSSL::Cipher
的实例。加密的 PEM 编码密钥将如下所示
-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0 [...] -----END RSA PRIVATE KEY-----
请注意,此格式使用 MD5 来推导出加密密钥,因此在符合 FIPS 的系统上将不可用。
此方法保留用于兼容性。 这仅应在需要 PKCS #1 RSAPrivateKey 格式时使用。
考虑使用 public_to_pem
(X.509 SubjectPublicKeyInfo)或 private_to_pem
(PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo)代替。
static VALUE ossl_rsa_export(int argc, VALUE *argv, VALUE self) { if (can_export_rsaprivatekey(self)) return ossl_pkey_export_traditional(argc, argv, self, 0); else return ossl_pkey_export_spki(self, 0); }
HAVE_EVP_PKEY_DUP static VALUE ossl_rsa_initialize_copy(VALUE self, VALUE other) { EVP_PKEY *pkey; RSA *rsa, *rsa_new; TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey); if (pkey) rb_raise(rb_eTypeError, "pkey already initialized"); GetRSA(other, rsa); rsa_new = (RSA *)ASN1_dup((i2d_of_void *)i2d_RSAPrivateKey, (d2i_of_void *)d2i_RSAPrivateKey, (char *)rsa); if (!rsa_new) ossl_raise(eRSAError, "ASN1_dup"); pkey = EVP_PKEY_new(); if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa_new) != 1) { RSA_free(rsa_new); ossl_raise(eRSAError, "EVP_PKEY_assign_RSA"); } RTYPEDDATA_DATA(self) = pkey; return self; }
此方法不安全,可能会泄露私有信息!
将密钥的所有参数存储到哈希中。哈希具有键 ‘n’、‘e’、‘d’、‘p’、‘q’、‘dmp1’、‘dmq1’、‘iqmp’。
不要使用 :-))(由你决定)
static VALUE ossl_rsa_get_params(VALUE self) { OSSL_3_const RSA *rsa; VALUE hash; const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; GetRSA(self, rsa); RSA_get0_key(rsa, &n, &e, &d); RSA_get0_factors(rsa, &p, &q); RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("n"), ossl_bn_new(n)); rb_hash_aset(hash, rb_str_new2("e"), ossl_bn_new(e)); rb_hash_aset(hash, rb_str_new2("d"), ossl_bn_new(d)); 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("dmp1"), ossl_bn_new(dmp1)); rb_hash_aset(hash, rb_str_new2("dmq1"), ossl_bn_new(dmq1)); rb_hash_aset(hash, rb_str_new2("iqmp"), ossl_bn_new(iqmp)); return hash; }
此密钥对是否包含私钥?
static VALUE ossl_rsa_is_private(VALUE self) { OSSL_3_const RSA *rsa; GetRSA(self, rsa); return RSA_PRIVATE(self, rsa) ? Qtrue : Qfalse; }
使用私钥解密使用公钥加密的string
。padding
默认为 PKCS1_PADDING
,已知不安全,但保留用于向后兼容。
在版本 3.0 中已弃用。考虑使用 PKey::PKey#encrypt
和 PKey::PKey#decrypt
代替。
# File ext/openssl/lib/openssl/pkey.rb, line 439 def private_decrypt(data, padding = PKCS1_PADDING) n or raise OpenSSL::PKey::RSAError, "incomplete RSA" private? or raise OpenSSL::PKey::RSAError, "private key needed." begin decrypt(data, { "rsa_padding_mode" => translate_padding_mode(padding), }) rescue OpenSSL::PKey::PKeyError raise OpenSSL::PKey::RSAError, $!.message end end
使用私钥加密string
。padding
默认为 PKCS1_PADDING
,已知不安全,但保留用于向后兼容。加密的字符串输出可以使用 public_decrypt
解密。
在版本 3.0 中已弃用。考虑使用 PKey::PKey#sign_raw
和 PKey::PKey#verify_raw
,以及 PKey::PKey#verify_recover
代替。
# File ext/openssl/lib/openssl/pkey.rb, line 373 def private_encrypt(string, padding = PKCS1_PADDING) n or raise OpenSSL::PKey::RSAError, "incomplete RSA" private? or raise OpenSSL::PKey::RSAError, "private key needed." begin sign_raw(nil, string, { "rsa_padding_mode" => translate_padding_mode(padding), }) rescue OpenSSL::PKey::PKeyError raise OpenSSL::PKey::RSAError, $!.message end end
返回值始终为true
,因为每个私钥也是公钥。
static VALUE ossl_rsa_is_public(VALUE self) { OSSL_3_const RSA *rsa; GetRSA(self, rsa); /* * This method should check for n and e. BUG. */ (void)rsa; return Qtrue; }
使用公钥解密使用私钥加密的string
。padding
默认为 PKCS1_PADDING
,已知不安全,但保留用于向后兼容。
在版本 3.0 中已弃用。考虑使用 PKey::PKey#sign_raw
和 PKey::PKey#verify_raw
,以及 PKey::PKey#verify_recover
代替。
# File ext/openssl/lib/openssl/pkey.rb, line 396 def public_decrypt(string, padding = PKCS1_PADDING) n or raise OpenSSL::PKey::RSAError, "incomplete RSA" begin verify_recover(nil, string, { "rsa_padding_mode" => translate_padding_mode(padding), }) rescue OpenSSL::PKey::PKeyError raise OpenSSL::PKey::RSAError, $!.message end end
使用公钥加密string
。padding
默认为 PKCS1_PADDING
,已知不安全,但保留用于向后兼容。加密的字符串输出可以使用 private_decrypt
解密。
在版本 3.0 中已弃用。考虑使用 PKey::PKey#encrypt
和 PKey::PKey#decrypt
代替。
# File ext/openssl/lib/openssl/pkey.rb, line 418 def public_encrypt(data, padding = PKCS1_PADDING) n or raise OpenSSL::PKey::RSAError, "incomplete RSA" begin encrypt(data, { "rsa_padding_mode" => translate_padding_mode(padding), }) rescue OpenSSL::PKey::PKeyError raise OpenSSL::PKey::RSAError, $!.message end end
返回一个新的 RSA
实例,它只包含公钥组件。
此方法提供用于向后兼容。在大多数情况下,无需调用此方法。
为了将公钥序列化为 X.509 SubjectPublicKeyInfo 格式的 PEM 或 DER 编码,请检查 PKey#public_to_pem
和 PKey#public_to_der
.
# File ext/openssl/lib/openssl/pkey.rb, line 327 def public_key OpenSSL::PKey.read(public_to_der) end
为 RSA
实例设置dmp1、dmq1、iqmp。它们分别通过 d mod (p - 1)
、d mod (q - 1)
和 q^(-1) mod p
计算。
为 RSA
实例设置p、q。
为 RSA
实例设置n、e、d。
使用概率签名方案 (RSA-PSS) 对data 进行签名,并返回计算出的签名。
如果发生错误,将引发 RSAError
。
有关验证操作,请参见 verify_pss
。
参数¶ ↑
- digest
-
包含消息摘要算法名称的
String
。 - data
-
一个
String
。要签名的数据。 - salt_length
-
盐的字节长度。保留两个特殊值:
:digest
表示摘要长度,:max
表示私钥和所选消息摘要算法组合的最大可能长度。 - mgf1_hash
-
MGF1 中使用的哈希算法(当前支持的掩码生成函数 (MGF))。
示例¶ ↑
data = "Sign me!" pkey = OpenSSL::PKey::RSA.new(2048) signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256") pub_key = OpenSSL::PKey.read(pkey.public_to_der) puts pub_key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256") # => true
static VALUE ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) { VALUE digest, data, options, kwargs[2], signature; static ID kwargs_ids[2]; EVP_PKEY *pkey; EVP_PKEY_CTX *pkey_ctx; const EVP_MD *md, *mgf1md; EVP_MD_CTX *md_ctx; size_t buf_len; int salt_len; if (!kwargs_ids[0]) { kwargs_ids[0] = rb_intern_const("salt_length"); kwargs_ids[1] = rb_intern_const("mgf1_hash"); } rb_scan_args(argc, argv, "2:", &digest, &data, &options); rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); if (kwargs[0] == ID2SYM(rb_intern("max"))) salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */ else if (kwargs[0] == ID2SYM(rb_intern("digest"))) salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ else salt_len = NUM2INT(kwargs[0]); mgf1md = ossl_evp_get_digestbyname(kwargs[1]); pkey = GetPrivPKeyPtr(self); buf_len = EVP_PKEY_size(pkey); md = ossl_evp_get_digestbyname(digest); StringValue(data); signature = rb_str_new(NULL, (long)buf_len); md_ctx = EVP_MD_CTX_new(); if (!md_ctx) goto err; if (EVP_DigestSignInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) goto err; if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) goto err; if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) goto err; if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) goto err; if (EVP_DigestSignUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) goto err; if (EVP_DigestSignFinal(md_ctx, (unsigned char *)RSTRING_PTR(signature), &buf_len) != 1) goto err; rb_str_set_len(signature, (long)buf_len); EVP_MD_CTX_free(md_ctx); return signature; err: EVP_MD_CTX_free(md_ctx); ossl_raise(eRSAError, NULL); }
将私钥或公钥序列化为 DER 编码。
有关详细信息,请参见 to_pem
。
此方法保留用于兼容性。 这仅应在需要 PKCS #1 RSAPrivateKey 格式时使用。
考虑使用 public_to_der
或 private_to_der
代替。
static VALUE ossl_rsa_to_der(VALUE self) { if (can_export_rsaprivatekey(self)) return ossl_pkey_export_traditional(0, NULL, self, 1); else return ossl_pkey_export_spki(self, 1); }
将私钥或公钥序列化为 PEM 编码。
- 当密钥仅包含公钥组件时
-
将其序列化为 X.509 SubjectPublicKeyInfo。参数cipher 和password 被忽略。
PEM 编码的密钥将如下所示
-----BEGIN PUBLIC KEY----- [...] -----END PUBLIC KEY-----
考虑使用
public_to_pem
代替。这将密钥序列化为 X.509 SubjectPublicKeyInfo,无论密钥是公钥还是私钥。 - 当密钥包含私钥组件,并且没有给出参数时
-
将其序列化为 PKCS #1 RSAPrivateKey。
PEM 编码的密钥将如下所示
-----BEGIN RSA PRIVATE KEY----- [...] -----END RSA PRIVATE KEY-----
- 当密钥包含私钥组件,并且给出cipher 和password 时
-
将其序列化为 PKCS #1 RSAPrivateKey 并使用 OpenSSL 的传统 PEM 加密格式对其进行加密。cipher 必须是
OpenSSL::Cipher.new
理解的密码名称,或OpenSSL::Cipher
的实例。加密的 PEM 编码密钥将如下所示
-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0 [...] -----END RSA PRIVATE KEY-----
请注意,此格式使用 MD5 来推导出加密密钥,因此在符合 FIPS 的系统上将不可用。
此方法保留用于兼容性。 这仅应在需要 PKCS #1 RSAPrivateKey 格式时使用。
考虑使用 public_to_pem
(X.509 SubjectPublicKeyInfo)或 private_to_pem
(PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo)代替。
将私钥或公钥序列化为 PEM 编码。
- 当密钥仅包含公钥组件时
-
将其序列化为 X.509 SubjectPublicKeyInfo。参数cipher 和password 被忽略。
PEM 编码的密钥将如下所示
-----BEGIN PUBLIC KEY----- [...] -----END PUBLIC KEY-----
考虑使用
public_to_pem
代替。这将密钥序列化为 X.509 SubjectPublicKeyInfo,无论密钥是公钥还是私钥。 - 当密钥包含私钥组件,并且没有给出参数时
-
将其序列化为 PKCS #1 RSAPrivateKey。
PEM 编码的密钥将如下所示
-----BEGIN RSA PRIVATE KEY----- [...] -----END RSA PRIVATE KEY-----
- 当密钥包含私钥组件,并且给出cipher 和password 时
-
将其序列化为 PKCS #1 RSAPrivateKey 并使用 OpenSSL 的传统 PEM 加密格式对其进行加密。cipher 必须是
OpenSSL::Cipher.new
理解的密码名称,或OpenSSL::Cipher
的实例。加密的 PEM 编码密钥将如下所示
-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0 [...] -----END RSA PRIVATE KEY-----
请注意,此格式使用 MD5 来推导出加密密钥,因此在符合 FIPS 的系统上将不可用。
此方法保留用于兼容性。 这仅应在需要 PKCS #1 RSAPrivateKey 格式时使用。
考虑使用 public_to_pem
(X.509 SubjectPublicKeyInfo)或 private_to_pem
(PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo)代替。
使用概率签名方案 (RSA-PSS) 验证data。
如果签名有效,则返回值为true
,否则为false
。如果发生错误,将引发 RSAError
。
有关签名操作和示例代码,请参见 sign_pss
。
参数¶ ↑
- digest
-
包含消息摘要算法名称的
String
。 - data
-
一个
String
。要签名的数据。 - salt_length
-
盐的字节长度。保留两个特殊值:
:digest
表示摘要长度,:auto
表示根据签名自动确定长度。 - mgf1_hash
-
MGF1 中使用的哈希算法。
static VALUE ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self) { VALUE digest, signature, data, options, kwargs[2]; static ID kwargs_ids[2]; EVP_PKEY *pkey; EVP_PKEY_CTX *pkey_ctx; const EVP_MD *md, *mgf1md; EVP_MD_CTX *md_ctx; int result, salt_len; if (!kwargs_ids[0]) { kwargs_ids[0] = rb_intern_const("salt_length"); kwargs_ids[1] = rb_intern_const("mgf1_hash"); } rb_scan_args(argc, argv, "3:", &digest, &signature, &data, &options); rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); if (kwargs[0] == ID2SYM(rb_intern("auto"))) salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */ else if (kwargs[0] == ID2SYM(rb_intern("digest"))) salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ else salt_len = NUM2INT(kwargs[0]); mgf1md = ossl_evp_get_digestbyname(kwargs[1]); GetPKey(self, pkey); md = ossl_evp_get_digestbyname(digest); StringValue(signature); StringValue(data); md_ctx = EVP_MD_CTX_new(); if (!md_ctx) goto err; if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) goto err; if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) goto err; if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) goto err; if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) goto err; if (EVP_DigestVerifyUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) goto err; result = EVP_DigestVerifyFinal(md_ctx, (unsigned char *)RSTRING_PTR(signature), RSTRING_LEN(signature)); switch (result) { case 0: ossl_clear_error(); EVP_MD_CTX_free(md_ctx); return Qfalse; case 1: EVP_MD_CTX_free(md_ctx); return Qtrue; default: goto err; } err: EVP_MD_CTX_free(md_ctx); ossl_raise(eRSAError, NULL); }
私有实例方法
# File ext/openssl/lib/openssl/pkey.rb, line 456 def translate_padding_mode(num) case num when PKCS1_PADDING "pkcs1" when SSLV23_PADDING "sslv23" when NO_PADDING "none" when PKCS1_OAEP_PADDING "oaep" else raise OpenSSL::PKey::PKeyError, "unsupported padding mode" end end