class OpenSSL::PKey::RSA
RSA 是一种非对称公钥算法,已在 RFC 3447 中正式化。它广泛用于公钥基础设施 (PKI) 中,其中证书(参见 OpenSSL::X509::Certificate)通常基于公钥/私钥 RSA 密钥对进行颁发。 RSA 用于各种应用领域,例如安全(对称)密钥交换,例如在建立安全的 TLS/SSL 连接时。它还用于各种数字签名方案。
Constants
- NO_PADDING
- PKCS1_OAEP_PADDING
- PKCS1_PADDING
- SSLV23_PADDING
Public Class Methods
Source
# File ext/openssl/lib/openssl/pkey.rb, line 381 def generate(size, exp = 0x10001, &blk) OpenSSL::PKey.generate_key("RSA", { "rsa_keygen_bits" => size, "rsa_keygen_pubexp" => exp, }, &blk) end
生成 RSA 密钥对。
另请参阅 OpenSSL::PKey.generate_key。
size-
所需的密钥大小(以位为单位)。
exponent-
一个奇数
Integer,通常为 3、17 或 65537。
Source
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) {
#ifdef OSSL_HAVE_IMMUTABLE_PKEY
rb_raise(rb_eArgError, "OpenSSL::PKey::RSA.new cannot be called " \
"without arguments; pkeys are immutable with OpenSSL 3.0");
#else
rsa = RSA_new();
if (!rsa)
ossl_raise(ePKeyError, "RSA_new");
goto legacy;
#endif
}
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(ePKeyError, "Neither PUB key nor PRIV key");
type = EVP_PKEY_base_id(pkey);
if (type != EVP_PKEY_RSA) {
EVP_PKEY_free(pkey);
rb_raise(ePKeyError, "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(ePKeyError, "EVP_PKEY_assign_RSA");
}
RTYPEDDATA_DATA(self) = pkey;
return self;
}
生成或加载 RSA 密钥对。
如果不带参数调用,则创建一个没有设置密钥组件的新实例。它们可以由 set_key、set_factors 和 set_crt_params 单独设置。此形式与 OpenSSL 3.0 或更高版本不兼容。
如果使用 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'
Public Instance Methods
Source
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);
}
将私有或公钥序列化为 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)。
Source
# File ext/openssl/lib/openssl/pkey.rb, line 363 def params %w{n e d p q dmp1 dmq1 iqmp}.map { |name| [name, send(name)] }.to_h end
将密钥的所有参数存储到 Hash 中。
哈希包含键 ‘n’、‘e’、‘d’、‘p’、‘q’、‘dmp1’、‘dmq1’ 和 ‘iqmp’。
Source
static VALUE
ossl_rsa_is_private(VALUE self)
{
OSSL_3_const RSA *rsa;
GetRSA(self, rsa);
return RSA_PRIVATE(self, rsa) ? Qtrue : Qfalse;
}
此密钥对是否包含私钥?
Source
# File ext/openssl/lib/openssl/pkey.rb, line 465 def private_decrypt(data, padding = PKCS1_PADDING) n or raise PKeyError, "incomplete RSA" private? or raise PKeyError, "private key needed." decrypt(data, { "rsa_padding_mode" => translate_padding_mode(padding), }) end
使用私钥解密使用公钥加密的 string。padding 默认为 PKCS1_PADDING,已知此方法不安全,但为向后兼容而保留。
版本 3.0 已弃用。请考虑使用 PKey::PKey#encrypt 和 PKey::PKey#decrypt。
Source
# File ext/openssl/lib/openssl/pkey.rb, line 411 def private_encrypt(string, padding = PKCS1_PADDING) n or raise PKeyError, "incomplete RSA" private? or raise PKeyError, "private key needed." sign_raw(nil, string, { "rsa_padding_mode" => translate_padding_mode(padding), }) end
使用私钥加密 string。padding 默认为 PKCS1_PADDING,已知此方法不安全,但为向后兼容而保留。加密后的字符串输出可以使用 public_decrypt 解密。
版本 3.0 已弃用。请考虑使用 PKey::PKey#sign_raw 和 PKey::PKey#verify_raw,以及 PKey::PKey#verify_recover。
Source
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;
}
返回值始终为 true,因为每个私钥也是一个公钥。
Source
# File ext/openssl/lib/openssl/pkey.rb, line 430 def public_decrypt(string, padding = PKCS1_PADDING) n or raise PKeyError, "incomplete RSA" verify_recover(nil, string, { "rsa_padding_mode" => translate_padding_mode(padding), }) end
使用公钥解密使用私钥加密的 string。padding 默认为 PKCS1_PADDING,已知此方法不安全,但为向后兼容而保留。
版本 3.0 已弃用。请考虑使用 PKey::PKey#sign_raw 和 PKey::PKey#verify_raw,以及 PKey::PKey#verify_recover。
Source
# File ext/openssl/lib/openssl/pkey.rb, line 448 def public_encrypt(data, padding = PKCS1_PADDING) n or raise PKeyError, "incomplete RSA" encrypt(data, { "rsa_padding_mode" => translate_padding_mode(padding), }) end
使用公钥加密 string。padding 默认为 PKCS1_PADDING,已知此方法不安全,但为向后兼容而保留。加密后的字符串输出可以使用 private_decrypt 解密。
版本 3.0 已弃用。请考虑使用 PKey::PKey#encrypt 和 PKey::PKey#decrypt。
Source
# File ext/openssl/lib/openssl/pkey.rb, line 353 def public_key OpenSSL::PKey.read(public_to_der) end
返回一个只包含公钥组件的新 RSA 实例。
此方法是为了向后兼容而提供的。在大多数情况下,无需调用此方法。
有关将公钥序列化为 X.509 SubjectPublicKeyInfo 格式的 PEM 或 DER 编码,请参阅 PKey#public_to_pem 和 PKey#public_to_der。
为 RSA 实例设置 dmp1、dmq1、iqmp。它们分别由 d mod (p - 1)、d mod (q - 1) 和 q^(-1) mod p 计算得出。
为 RSA 实例设置 p、q。
为 RSA 实例设置 n、e、d。
Source
static VALUE
ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self)
{
VALUE digest, data, options, kwargs[2], signature, mgf1md_holder, md_holder;
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_md_fetch(kwargs[1], &mgf1md_holder);
pkey = GetPrivPKeyPtr(self);
buf_len = EVP_PKEY_size(pkey);
md = ossl_evp_md_fetch(digest, &md_holder);
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(ePKeyError, NULL);
}
使用概率签名方案 (RSA-PSS) 对 data 进行签名,并返回计算出的签名。
如果发生错误,将引发 PKeyError。
另请参阅 verify_pss 以了解验证操作。
参数
- digest
-
包含消息摘要算法名称的
String。 - 数据
-
要签名的
String。 - salt_length
-
salt 的字节长度。保留两个特殊值:
: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
Source
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);
}
将私有或公钥序列化为 DER 编码。
详情请参阅 to_pem。
为兼容性保留此方法。 仅应在需要 PKCS #1 RSAPrivateKey 格式时使用此方法。
请考虑改用 public_to_der 或 private_to_der。
将私有或公钥序列化为 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)。
Source
static VALUE
ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self)
{
VALUE digest, signature, data, options, kwargs[2], mgf1md_holder, md_holder;
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_md_fetch(kwargs[1], &mgf1md_holder);
GetPKey(self, pkey);
md = ossl_evp_md_fetch(digest, &md_holder);
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));
EVP_MD_CTX_free(md_ctx);
switch (result) {
case 0:
ossl_clear_error();
return Qfalse;
case 1:
return Qtrue;
default:
ossl_raise(ePKeyError, "EVP_DigestVerifyFinal");
}
err:
EVP_MD_CTX_free(md_ctx);
ossl_raise(ePKeyError, NULL);
}
私有实例方法
Source
# File ext/openssl/lib/openssl/pkey.rb, line 478 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 PKeyError, "unsupported padding mode" end end