mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 04:24:23 +00:00
Remove the following subclasses of OpenSSL::PKey::PKeyError and make
them aliases of it.
- OpenSSL::PKey::DHError
- OpenSSL::PKey::DSAError
- OpenSSL::PKey::ECError
- OpenSSL::PKey::RSAError
Historically, methods defined on OpenSSL::PKey and OpenSSL::PKey::PKey
raise OpenSSL::PKey::PKeyError, while methods on the subclasses raise
their respective exception classes. However, this distinction is not
particularly useful since all those exception classes represent the
same kind of errors from the underlying EVP_PKEY API.
I think this convention comes from the fact that OpenSSL::PKey::{DH,
DSA,RSA} originally wrapped the corresponding OpenSSL structs DH, DSA,
and RSA, before they were unified to wrap EVP_PKEY, way back in 2002.
OpenSSL::PKey::EC::Group::Error and OpenSSL::PKey::EC::Point::Error
are out of scope of this change, as they are not subclasses of
OpenSSL::PKey::PKeyError and do not represent errors from the EVP_PKEY
API.
https://github.com/ruby/openssl/commit/e74ff3e272
561 lines
19 KiB
Ruby
561 lines
19 KiB
Ruby
# frozen_string_literal: true
|
|
require_relative "utils"
|
|
|
|
if defined?(OpenSSL)
|
|
|
|
class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
|
|
def test_no_private_exp
|
|
key = OpenSSL::PKey::RSA.new
|
|
rsa = Fixtures.pkey("rsa-1")
|
|
key.set_key(rsa.n, rsa.e, nil)
|
|
key.set_factors(rsa.p, rsa.q)
|
|
assert_raise(OpenSSL::PKey::PKeyError){ key.private_encrypt("foo") }
|
|
assert_raise(OpenSSL::PKey::PKeyError){ key.private_decrypt("foo") }
|
|
end if !openssl?(3, 0, 0) # Impossible state in OpenSSL 3.0
|
|
|
|
def test_private
|
|
key = Fixtures.pkey("rsa-1")
|
|
|
|
# Generated by DER
|
|
key2 = OpenSSL::PKey::RSA.new(key.to_der)
|
|
assert_true(key2.private?)
|
|
|
|
# public key
|
|
key3 = key.public_key
|
|
assert_false(key3.private?)
|
|
|
|
# Generated by public key DER
|
|
key4 = OpenSSL::PKey::RSA.new(key3.to_der)
|
|
assert_false(key4.private?)
|
|
|
|
if !openssl?(3, 0, 0)
|
|
# Generated by RSA#set_key
|
|
key5 = OpenSSL::PKey::RSA.new
|
|
key5.set_key(key.n, key.e, key.d)
|
|
assert_true(key5.private?)
|
|
|
|
# Generated by RSA#set_key, without d
|
|
key6 = OpenSSL::PKey::RSA.new
|
|
key6.set_key(key.n, key.e, nil)
|
|
assert_false(key6.private?)
|
|
end
|
|
end
|
|
|
|
def test_new
|
|
key = OpenSSL::PKey::RSA.new(2048)
|
|
assert_equal 2048, key.n.num_bits
|
|
assert_equal 65537, key.e
|
|
assert_not_nil key.d
|
|
assert(key.private?)
|
|
end
|
|
|
|
def test_new_public_exponent
|
|
# At least 2024-bits RSA key are required in FIPS.
|
|
omit_on_fips
|
|
|
|
# Specify public exponent
|
|
key = OpenSSL::PKey::RSA.new(512, 3)
|
|
assert_equal 512, key.n.num_bits
|
|
assert_equal 3, key.e
|
|
end
|
|
|
|
def test_new_empty
|
|
# pkeys are immutable with OpenSSL >= 3.0
|
|
if openssl?(3, 0, 0)
|
|
assert_raise(ArgumentError) { OpenSSL::PKey::RSA.new }
|
|
else
|
|
key = OpenSSL::PKey::RSA.new
|
|
assert_nil(key.n)
|
|
end
|
|
end
|
|
|
|
def test_s_generate
|
|
key1 = OpenSSL::PKey::RSA.generate(2048)
|
|
assert_equal 2048, key1.n.num_bits
|
|
assert_equal 65537, key1.e
|
|
end
|
|
|
|
def test_s_generate_public_exponent
|
|
# At least 2024-bits RSA key are required in FIPS.
|
|
omit_on_fips
|
|
|
|
# Specify public exponent
|
|
key = OpenSSL::PKey::RSA.generate(512, 3)
|
|
assert_equal 512, key.n.num_bits
|
|
assert_equal 3, key.e
|
|
end
|
|
|
|
def test_new_break
|
|
assert_nil(OpenSSL::PKey::RSA.new(2048) { break })
|
|
assert_raise(RuntimeError) do
|
|
OpenSSL::PKey::RSA.new(2048) { raise }
|
|
end
|
|
end
|
|
|
|
def test_sign_verify
|
|
rsa = Fixtures.pkey("rsa2048")
|
|
data = "Sign me!"
|
|
signature = rsa.sign("SHA256", data)
|
|
assert_equal true, rsa.verify("SHA256", signature, data)
|
|
|
|
signature0 = (<<~'end;').unpack1("m")
|
|
ooy49i8aeFtkDYUU0RPDsEugGiNw4lZxpbQPnIwtdftEkka945IqKZ/MY3YSw7wKsvBZeaTy8GqL
|
|
lSWLThsRFDV+UUS9zUBbQ9ygNIT8OjdV+tNL63ZpKGprczSnw4F05MQIpajNRud/8jiI9rf+Wysi
|
|
WwXecjMl2FlXlLJHY4PFQZU5TiametB4VCQRMcjLo1uf26u/yRpiGaYyqn5vxs0SqNtUDM1UL6x4
|
|
NHCAdqLjuFRQPjYp1vGLD3eSl4061pS8x1NVap3YGbYfGUyzZO4VfwFwf1jPdhp/OX/uZw4dGB2H
|
|
gSK+q1JiDFwEE6yym5tdKovL1g1NhFYHF6gkZg==
|
|
end;
|
|
assert_equal true, rsa.verify("SHA256", signature0, data)
|
|
signature1 = signature0.succ
|
|
assert_equal false, rsa.verify("SHA256", signature1, data)
|
|
end
|
|
|
|
def test_sign_verify_options
|
|
key = Fixtures.pkey("rsa2048")
|
|
data = "Sign me!"
|
|
pssopts = {
|
|
"rsa_padding_mode" => "pss",
|
|
"rsa_pss_saltlen" => 20,
|
|
"rsa_mgf1_md" => "SHA256"
|
|
}
|
|
sig_pss = key.sign("SHA256", data, pssopts)
|
|
assert_equal 256, sig_pss.bytesize
|
|
assert_equal true, key.verify("SHA256", sig_pss, data, pssopts)
|
|
assert_equal true, key.verify_pss("SHA256", sig_pss, data,
|
|
salt_length: 20, mgf1_hash: "SHA256")
|
|
# Defaults to PKCS #1 v1.5 padding => verification failure
|
|
assert_equal false, key.verify("SHA256", sig_pss, data)
|
|
|
|
# option type check
|
|
assert_raise_with_message(TypeError, /expected Hash/) {
|
|
key.sign("SHA256", data, ["x"])
|
|
}
|
|
end
|
|
|
|
def test_sign_verify_raw
|
|
key = Fixtures.pkey("rsa-1")
|
|
data = "Sign me!"
|
|
hash = OpenSSL::Digest.digest("SHA256", data)
|
|
signature = key.sign_raw("SHA256", hash)
|
|
assert_equal true, key.verify_raw("SHA256", signature, hash)
|
|
assert_equal true, key.verify("SHA256", signature, data)
|
|
|
|
# Too long data
|
|
assert_raise(OpenSSL::PKey::PKeyError) {
|
|
key.sign_raw("SHA1", "x" * (key.n.num_bytes + 1))
|
|
}
|
|
|
|
# With options
|
|
pssopts = {
|
|
"rsa_padding_mode" => "pss",
|
|
"rsa_pss_saltlen" => 20,
|
|
"rsa_mgf1_md" => "SHA256"
|
|
}
|
|
sig_pss = key.sign_raw("SHA256", hash, pssopts)
|
|
assert_equal true, key.verify("SHA256", sig_pss, data, pssopts)
|
|
assert_equal true, key.verify_raw("SHA256", sig_pss, hash, pssopts)
|
|
end
|
|
|
|
def test_sign_verify_raw_legacy
|
|
key = Fixtures.pkey("rsa-1")
|
|
bits = key.n.num_bits
|
|
|
|
# Need right size for raw mode
|
|
plain0 = "x" * (bits/8)
|
|
cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING)
|
|
plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING)
|
|
assert_equal(plain0, plain1)
|
|
|
|
# Need smaller size for pkcs1 mode
|
|
plain0 = "x" * (bits/8 - 11)
|
|
cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING)
|
|
plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING)
|
|
assert_equal(plain0, plain1)
|
|
|
|
cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default
|
|
plain1 = key.public_decrypt(cipherdef)
|
|
assert_equal(plain0, plain1)
|
|
assert_equal(cipher1, cipherdef)
|
|
|
|
# Failure cases
|
|
assert_raise(ArgumentError){ key.private_encrypt() }
|
|
assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) }
|
|
assert_raise(OpenSSL::PKey::PKeyError){ key.private_encrypt(plain0, 666) }
|
|
end
|
|
|
|
|
|
def test_verify_empty_rsa
|
|
rsa = OpenSSL::PKey::RSA.new
|
|
assert_raise(OpenSSL::PKey::PKeyError, "[Bug #12783]") {
|
|
rsa.verify("SHA1", "a", "b")
|
|
}
|
|
end unless openssl?(3, 0, 0) # Empty RSA is not possible with OpenSSL >= 3.0
|
|
|
|
def test_sign_verify_pss
|
|
key = Fixtures.pkey("rsa2048")
|
|
data = "Sign me!"
|
|
invalid_data = "Sign me?"
|
|
|
|
signature = key.sign_pss("SHA256", data, salt_length: 20, mgf1_hash: "SHA256")
|
|
assert_equal 256, signature.bytesize
|
|
assert_equal true,
|
|
key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA256")
|
|
assert_equal true,
|
|
key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256")
|
|
assert_equal false,
|
|
key.verify_pss("SHA256", signature, invalid_data, salt_length: 20, mgf1_hash: "SHA256")
|
|
|
|
signature = key.sign_pss("SHA256", data, salt_length: :digest, mgf1_hash: "SHA256")
|
|
assert_equal true,
|
|
key.verify_pss("SHA256", signature, data, salt_length: 32, mgf1_hash: "SHA256")
|
|
assert_equal true,
|
|
key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256")
|
|
assert_equal false,
|
|
key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA256")
|
|
|
|
# The sign_pss with `salt_length: :max` raises the "invalid salt length"
|
|
# error in FIPS. We need to skip the tests in FIPS.
|
|
# According to FIPS 186-5 section 5.4, the salt length shall be between zero
|
|
# and the output block length of the digest function (inclusive).
|
|
#
|
|
# FIPS 186-5 section 5.4 PKCS #1
|
|
# https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
|
|
unless OpenSSL.fips_mode
|
|
signature = key.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256")
|
|
# Should verify on the following salt_length (sLen).
|
|
# sLen <= emLen (octat) - 2 - hLen (octet) = 2048 / 8 - 2 - 256 / 8 = 222
|
|
# https://datatracker.ietf.org/doc/html/rfc8017#section-9.1.1
|
|
assert_equal true,
|
|
key.verify_pss("SHA256", signature, data, salt_length: 222, mgf1_hash: "SHA256")
|
|
assert_equal true,
|
|
key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256")
|
|
end
|
|
|
|
assert_raise(OpenSSL::PKey::PKeyError) {
|
|
key.sign_pss("SHA256", data, salt_length: 223, mgf1_hash: "SHA256")
|
|
}
|
|
end
|
|
|
|
def test_encrypt_decrypt
|
|
rsapriv = Fixtures.pkey("rsa-1")
|
|
rsapub = OpenSSL::PKey.read(rsapriv.public_to_der)
|
|
|
|
# Defaults to PKCS #1 v1.5
|
|
raw = "data"
|
|
# According to the NIST SP 800-131A Rev. 2 section 6, PKCS#1 v1.5 padding is
|
|
# not permitted for key agreement and key transport using RSA in FIPS.
|
|
# https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf
|
|
unless OpenSSL.fips_mode
|
|
enc = rsapub.encrypt(raw)
|
|
assert_equal raw, rsapriv.decrypt(enc)
|
|
end
|
|
|
|
# Invalid options
|
|
assert_raise(OpenSSL::PKey::PKeyError) {
|
|
rsapub.encrypt(raw, { "nonexistent" => "option" })
|
|
}
|
|
end
|
|
|
|
def test_encrypt_decrypt_legacy
|
|
rsapriv = Fixtures.pkey("rsa-1")
|
|
rsapub = OpenSSL::PKey.read(rsapriv.public_to_der)
|
|
|
|
# Defaults to PKCS #1 v1.5
|
|
unless OpenSSL.fips_mode
|
|
raw = "data"
|
|
enc_legacy = rsapub.public_encrypt(raw)
|
|
assert_equal raw, rsapriv.decrypt(enc_legacy)
|
|
enc_new = rsapub.encrypt(raw)
|
|
assert_equal raw, rsapriv.private_decrypt(enc_new)
|
|
end
|
|
|
|
# OAEP with default parameters
|
|
raw = "data"
|
|
enc_legacy = rsapub.public_encrypt(raw, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
|
|
assert_equal raw, rsapriv.decrypt(enc_legacy, { "rsa_padding_mode" => "oaep" })
|
|
enc_new = rsapub.encrypt(raw, { "rsa_padding_mode" => "oaep" })
|
|
assert_equal raw, rsapriv.private_decrypt(enc_legacy, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
|
|
end
|
|
|
|
def test_export
|
|
orig = Fixtures.pkey("rsa-1")
|
|
|
|
pub = OpenSSL::PKey.read(orig.public_to_der)
|
|
assert_not_equal orig.export, pub.export
|
|
assert_equal orig.public_to_pem, pub.export
|
|
|
|
# PKey is immutable in OpenSSL >= 3.0
|
|
if !openssl?(3, 0, 0)
|
|
key = OpenSSL::PKey::RSA.new
|
|
|
|
# key has only n, e and d
|
|
key.set_key(orig.n, orig.e, orig.d)
|
|
assert_equal orig.public_key.export, key.export
|
|
|
|
# key has only n, e, d, p and q
|
|
key.set_factors(orig.p, orig.q)
|
|
assert_equal orig.public_key.export, key.export
|
|
|
|
# key has n, e, d, p, q, dmp1, dmq1 and iqmp
|
|
key.set_crt_params(orig.dmp1, orig.dmq1, orig.iqmp)
|
|
assert_equal orig.export, key.export
|
|
end
|
|
end
|
|
|
|
def test_to_der
|
|
orig = Fixtures.pkey("rsa-1")
|
|
|
|
pub = OpenSSL::PKey.read(orig.public_to_der)
|
|
assert_not_equal orig.to_der, pub.to_der
|
|
assert_equal orig.public_to_der, pub.to_der
|
|
|
|
# PKey is immutable in OpenSSL >= 3.0
|
|
if !openssl?(3, 0, 0)
|
|
key = OpenSSL::PKey::RSA.new
|
|
|
|
# key has only n, e and d
|
|
key.set_key(orig.n, orig.e, orig.d)
|
|
assert_equal orig.public_key.to_der, key.to_der
|
|
|
|
# key has only n, e, d, p and q
|
|
key.set_factors(orig.p, orig.q)
|
|
assert_equal orig.public_key.to_der, key.to_der
|
|
|
|
# key has n, e, d, p, q, dmp1, dmq1 and iqmp
|
|
key.set_crt_params(orig.dmp1, orig.dmq1, orig.iqmp)
|
|
assert_equal orig.to_der, key.to_der
|
|
end
|
|
end
|
|
|
|
def test_RSAPrivateKey
|
|
rsa = Fixtures.pkey("rsa-1")
|
|
asn1 = OpenSSL::ASN1::Sequence([
|
|
OpenSSL::ASN1::Integer(0),
|
|
OpenSSL::ASN1::Integer(rsa.n),
|
|
OpenSSL::ASN1::Integer(rsa.e),
|
|
OpenSSL::ASN1::Integer(rsa.d),
|
|
OpenSSL::ASN1::Integer(rsa.p),
|
|
OpenSSL::ASN1::Integer(rsa.q),
|
|
OpenSSL::ASN1::Integer(rsa.dmp1),
|
|
OpenSSL::ASN1::Integer(rsa.dmq1),
|
|
OpenSSL::ASN1::Integer(rsa.iqmp)
|
|
])
|
|
key = OpenSSL::PKey::RSA.new(asn1.to_der)
|
|
assert_predicate key, :private?
|
|
assert_same_rsa rsa, key
|
|
|
|
pem = der_to_pem(asn1.to_der, "RSA PRIVATE KEY")
|
|
key = OpenSSL::PKey::RSA.new(pem)
|
|
assert_same_rsa rsa, key
|
|
|
|
assert_equal asn1.to_der, rsa.to_der
|
|
assert_equal pem, rsa.export
|
|
|
|
# Unknown PEM prepended
|
|
cert = issue_cert(OpenSSL::X509::Name.new([["CN", "nobody"]]), rsa, 1, [], nil, nil)
|
|
str = cert.to_text + cert.to_pem + rsa.to_pem
|
|
key = OpenSSL::PKey::RSA.new(str)
|
|
assert_same_rsa rsa, key
|
|
end
|
|
|
|
def test_RSAPrivateKey_encrypted
|
|
# PKCS #1 RSAPrivateKey with OpenSSL encryption
|
|
omit_on_fips
|
|
|
|
rsa = Fixtures.pkey("rsa2048")
|
|
|
|
pem = der_to_encrypted_pem(rsa.to_der, "RSA PRIVATE KEY", "abcdef")
|
|
key = OpenSSL::PKey::RSA.new(pem, "abcdef")
|
|
assert_same_rsa rsa, key
|
|
key = OpenSSL::PKey::RSA.new(pem) { "abcdef" }
|
|
assert_same_rsa rsa, key
|
|
|
|
cipher = OpenSSL::Cipher.new("aes-128-cbc")
|
|
exported = rsa.to_pem(cipher, "abcdef\0\1")
|
|
assert_same_rsa rsa, OpenSSL::PKey::RSA.new(exported, "abcdef\0\1")
|
|
assert_raise(OpenSSL::PKey::PKeyError) {
|
|
OpenSSL::PKey::RSA.new(exported, "abcdef")
|
|
}
|
|
end
|
|
|
|
def test_RSAPublicKey
|
|
# PKCS #1 RSAPublicKey. Only decoding is supported
|
|
orig = Fixtures.pkey("rsa-1")
|
|
pub = OpenSSL::PKey::RSA.new(orig.public_to_der)
|
|
|
|
asn1 = OpenSSL::ASN1::Sequence([
|
|
OpenSSL::ASN1::Integer(orig.n),
|
|
OpenSSL::ASN1::Integer(orig.e)
|
|
])
|
|
key = OpenSSL::PKey::RSA.new(asn1.to_der)
|
|
assert_not_predicate key, :private?
|
|
assert_same_rsa pub, key
|
|
|
|
pem = der_to_pem(asn1.to_der, "RSA PUBLIC KEY")
|
|
key = OpenSSL::PKey::RSA.new(pem)
|
|
assert_same_rsa pub, key
|
|
end
|
|
|
|
def test_PUBKEY
|
|
orig = Fixtures.pkey("rsa-1")
|
|
pub = OpenSSL::PKey::RSA.new(orig.public_to_der)
|
|
|
|
asn1 = OpenSSL::ASN1::Sequence([
|
|
OpenSSL::ASN1::Sequence([
|
|
OpenSSL::ASN1::ObjectId("rsaEncryption"),
|
|
OpenSSL::ASN1::Null(nil)
|
|
]),
|
|
OpenSSL::ASN1::BitString(
|
|
OpenSSL::ASN1::Sequence([
|
|
OpenSSL::ASN1::Integer(orig.n),
|
|
OpenSSL::ASN1::Integer(orig.e)
|
|
]).to_der
|
|
)
|
|
])
|
|
key = OpenSSL::PKey::RSA.new(asn1.to_der)
|
|
assert_not_predicate key, :private?
|
|
assert_same_rsa pub, key
|
|
|
|
pem = der_to_pem(asn1.to_der, "PUBLIC KEY")
|
|
key = OpenSSL::PKey::RSA.new(pem)
|
|
assert_same_rsa pub, key
|
|
|
|
assert_equal asn1.to_der, key.to_der
|
|
assert_equal pem, key.export
|
|
|
|
assert_equal asn1.to_der, orig.public_to_der
|
|
assert_equal asn1.to_der, key.public_to_der
|
|
assert_equal pem, orig.public_to_pem
|
|
assert_equal pem, key.public_to_pem
|
|
end
|
|
|
|
def test_pem_passwd
|
|
omit_on_fips
|
|
|
|
key = Fixtures.pkey("rsa-1")
|
|
pem3c = key.to_pem("aes-128-cbc", "key")
|
|
assert_match (/ENCRYPTED/), pem3c
|
|
assert_equal key.to_der, OpenSSL::PKey.read(pem3c, "key").to_der
|
|
assert_equal key.to_der, OpenSSL::PKey.read(pem3c) { "key" }.to_der
|
|
assert_raise(OpenSSL::PKey::PKeyError) {
|
|
OpenSSL::PKey.read(pem3c) { nil }
|
|
}
|
|
end
|
|
|
|
def test_private_encoding
|
|
pkey = Fixtures.pkey("rsa-1")
|
|
asn1 = OpenSSL::ASN1::Sequence([
|
|
OpenSSL::ASN1::Integer(0),
|
|
OpenSSL::ASN1::Sequence([
|
|
OpenSSL::ASN1::ObjectId("rsaEncryption"),
|
|
OpenSSL::ASN1::Null(nil)
|
|
]),
|
|
OpenSSL::ASN1::OctetString(pkey.to_der)
|
|
])
|
|
assert_equal asn1.to_der, pkey.private_to_der
|
|
assert_same_rsa pkey, OpenSSL::PKey.read(asn1.to_der)
|
|
|
|
pem = der_to_pem(asn1.to_der, "PRIVATE KEY")
|
|
assert_equal pem, pkey.private_to_pem
|
|
assert_same_rsa pkey, OpenSSL::PKey.read(pem)
|
|
end
|
|
|
|
def test_private_encoding_encrypted
|
|
rsa = Fixtures.pkey("rsa2048")
|
|
encoded = rsa.private_to_der("aes-128-cbc", "abcdef")
|
|
asn1 = OpenSSL::ASN1.decode(encoded) # PKCS #8 EncryptedPrivateKeyInfo
|
|
assert_kind_of OpenSSL::ASN1::Sequence, asn1
|
|
assert_equal 2, asn1.value.size
|
|
assert_not_equal rsa.private_to_der, encoded
|
|
assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdef")
|
|
assert_same_rsa rsa, OpenSSL::PKey.read(encoded) { "abcdef" }
|
|
assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.read(encoded, "abcxyz") }
|
|
|
|
encoded = rsa.private_to_pem("aes-128-cbc", "abcdef")
|
|
assert_match (/BEGIN ENCRYPTED PRIVATE KEY/), encoded.lines[0]
|
|
assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdef")
|
|
|
|
# Use openssl instead of certtool due to https://gitlab.com/gnutls/gnutls/-/issues/1632
|
|
# openssl pkcs8 -in test/openssl/fixtures/pkey/rsa2048.pem -topk8 -v2 aes-128-cbc -passout pass:abcdef
|
|
pem = <<~EOF
|
|
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
|
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIay5V8CDQi5oCAggA
|
|
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBB6eyagcbsvdQlM1kPcH7kiBIIE
|
|
0Ng1apIyoPAZ4BfC4kMNeSmeAv3XspxqYi3uWzXiNyTcoE6390swrwM6WvdpXvLI
|
|
/n/V06krxPZ9X4fBG2kLUzXt5f09lEvmQU1HW1wJGU5Sq3bNeXBrlJF4DzJE4WWd
|
|
whVVvNMm44ghdzN/jGSw3z+6d717N+waa7vrpBDsHjhsPNwxpyzUvcFPFysTazxx
|
|
kN/dziIBF6SRKi6w8VaJEMQ8czGu5T3jOc2e/1p3/AYhHLPS4NHhLR5OUh0TKqLK
|
|
tANAqI9YqCAjhqcYCmN3mMQXY52VfOqG9hlX1x9ZQyqiH7l102EWbPqouk6bCBLQ
|
|
wHepPg4uK99Wsdh65qEryNnXQ5ZmO6aGb6T3TFENCaNKmi8Nh+/5dr7J7YfhIwpo
|
|
FqHvk0hrZ8r3EQlr8/td0Yb1/IKzeQ34638uXf9UxK7C6o+ilsmJDR4PHJUfZL23
|
|
Yb9qWJ0GEzd5AMsI7x6KuUxSuH9nKniv5Tzyty3Xmb4FwXUyADWE19cVuaT+HrFz
|
|
GraKnA3UXbEgWAU48/l4K2HcAHyHDD2Kbp8k+o1zUkH0fWUdfE6OUGtx19Fv44Jh
|
|
B7xDngK8K48C6nrj06/DSYfXlb2X7WQiapeG4jt6U57tLH2XAjHCkvu0IBZ+//+P
|
|
yIWduEHQ3w8FBRcIsTNJo5CjkGk580TVQB/OBLWfX48Ay3oF9zgnomDIlVjl9D0n
|
|
lKxw/KMCLkvB78rUeGbr1Kwj36FhGpTBw3FgcYGa5oWFZTlcOgMTXLqlbb9JnDlA
|
|
Zs7Tu0WTyOTV/Dne9nEm39Dzu6wRojiIpmygTD4FI7rmOy3CYNvL3XPv7XQj0hny
|
|
Ee/fLxugYlQnwPZSqOVEQY2HsG7AmEHRsvy4bIWIGt+yzAPZixt9MUdJh91ttRt7
|
|
QA/8J1pAsGqEuQpF6UUINZop3J7twfhO4zWYN/NNQ52eWNX2KLfjfGRhrvatzmZ0
|
|
BuCsCI9hwEeE6PTlhbX1Rs177MrDc3vlqz2V3Po0OrFjXAyg9DR/OC4iK5wOG2ZD
|
|
7StVSP8bzwQXsz3fJ0ardKXgnU2YDAP6Vykjgt+nFI09HV/S2faOc2g/UK4Y2khl
|
|
J93u/GHMz/Kr3bKWGY1/6nPdIdFheQjsiNhd5gI4tWik2B3QwU9mETToZ2LSvDHU
|
|
jYCys576xJLkdMM6nJdq72z4tCoES9IxyHVs4uLjHKIo/ZtKr+8xDo8IL4ax3U8+
|
|
NMhs/lwReHmPGahm1fu9zLRbNCVL7e0zrOqbjvKcSEftObpV/LLcPYXtEm+lZcck
|
|
/PMw49HSE364anKEXCH1cyVWJwdZRpFUHvRpLIrpHru7/cthhiEMdLgK1/x8sLob
|
|
DiyieLxH1DPeXT4X+z94ER4IuPVOcV5AXc/omghispEX6DNUnn5jC4e3WyabjUbw
|
|
MuO9lVH9Wi2/ynExCqVmQkdbTXuLwjni1fJ27Q5zb0aCmhO8eq6P869NCjhJuiUj
|
|
NI9XtGLP50YVWE0kL8KEJqnyFudky8Khzk4/dyixQFqin5GfT4vetrLunGHy7lRB
|
|
3LpnFrpMOr+0xr1RW1k9vlmjRsJSiojJfReYO7gH3B5swiww2azogoL+4jhF1Jxh
|
|
OYLWdkKhP2jSVGqtIDtny0O4lBm2+hLpWjiI0mJQ7wdA
|
|
-----END ENCRYPTED PRIVATE KEY-----
|
|
EOF
|
|
assert_same_rsa rsa, OpenSSL::PKey.read(pem, "abcdef")
|
|
end
|
|
|
|
def test_params
|
|
key = Fixtures.pkey("rsa2048")
|
|
assert_equal(2048, key.n.num_bits)
|
|
assert_equal(key.n, key.params["n"])
|
|
assert_equal(65537, key.e)
|
|
assert_equal(key.e, key.params["e"])
|
|
[:d, :p, :q, :dmp1, :dmq1, :iqmp].each do |name|
|
|
assert_kind_of(OpenSSL::BN, key.send(name))
|
|
assert_equal(key.send(name), key.params[name.to_s])
|
|
end
|
|
|
|
pubkey = OpenSSL::PKey.read(key.public_to_der)
|
|
assert_equal(key.n, pubkey.n)
|
|
assert_equal(key.e, pubkey.e)
|
|
[:d, :p, :q, :dmp1, :dmq1, :iqmp].each do |name|
|
|
assert_nil(pubkey.send(name))
|
|
assert_nil(pubkey.params[name.to_s])
|
|
end
|
|
end
|
|
|
|
def test_dup
|
|
key = Fixtures.pkey("rsa-1")
|
|
key2 = key.dup
|
|
assert_equal key.params, key2.params
|
|
|
|
# PKey is immutable in OpenSSL >= 3.0
|
|
if !openssl?(3, 0, 0)
|
|
key2.set_key(key2.n, 3, key2.d)
|
|
assert_not_equal key.params, key2.params
|
|
end
|
|
end
|
|
|
|
def test_marshal
|
|
key = Fixtures.pkey("rsa-1")
|
|
deserialized = Marshal.load(Marshal.dump(key))
|
|
|
|
assert_equal key.to_der, deserialized.to_der
|
|
end
|
|
|
|
private
|
|
def assert_same_rsa(expected, key)
|
|
check_component(expected, key, [:n, :e, :d, :p, :q, :dmp1, :dmq1, :iqmp])
|
|
end
|
|
end
|
|
|
|
end
|