From 558128594de16add5b453833fd5b043a24c1b7f5 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Wed, 22 Dec 2021 01:38:47 +0900 Subject: [PATCH 1/3] Use OpenSSL::PKey::EC.generate to generate ECC key pairs When Ruby/OpenSSL is built against OpenSSL 3.0, OpenSSL::PKey::PKey instances are immutable and OpenSSL::PKey::EC#generate_key cannot work because it modifies the receiver. OpenSSL::PKey::EC.generate is available on Ruby 2.4 (Ruby/OpenSSL 2.0) or later. --- lib/rubygems/security.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb index 22759972070..2aa07381d69 100644 --- a/lib/rubygems/security.rb +++ b/lib/rubygems/security.rb @@ -490,9 +490,13 @@ def self.create_key(algorithm) when 'rsa' OpenSSL::PKey::RSA.new(RSA_DSA_KEY_LENGTH) when 'ec' - domain_key = OpenSSL::PKey::EC.new(EC_NAME) - domain_key.generate_key - domain_key + if RUBY_VERSION >= "2.4.0" + OpenSSL::PKey::EC.generate(EC_NAME) + else + domain_key = OpenSSL::PKey::EC.new(EC_NAME) + domain_key.generate_key + domain_key + end else raise Gem::Security::Exception, "#{algorithm} algorithm not found. RSA, DSA, and EC algorithms are supported." From 60067d4f09b7fb9c23bed38e91acfde0293f29a0 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Wed, 22 Dec 2021 01:49:05 +0900 Subject: [PATCH 2/3] Use OpenSSL::X509::Certificate#check_private_key The method is for the exact purpose: to check that an instance of OpenSSL::PKey::PKey matches the public key in a certificate. --- lib/rubygems/security.rb | 2 +- lib/rubygems/security/policy.rb | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb index 2aa07381d69..2906819bd34 100644 --- a/lib/rubygems/security.rb +++ b/lib/rubygems/security.rb @@ -530,7 +530,7 @@ def self.re_sign(expired_certificate, private_key, age = ONE_YEAR, raise Gem::Security::Exception, "incorrect signing key for re-signing " + "#{expired_certificate.subject}" unless - expired_certificate.public_key.to_pem == get_public_key(private_key).to_pem + expired_certificate.check_private_key(private_key) unless expired_certificate.subject.to_s == expired_certificate.issuer.to_s diff --git a/lib/rubygems/security/policy.rb b/lib/rubygems/security/policy.rb index 3c3cb647ee3..06eae073f4a 100644 --- a/lib/rubygems/security/policy.rb +++ b/lib/rubygems/security/policy.rb @@ -115,11 +115,9 @@ def check_key(signer, key) raise Gem::Security::Exception, 'missing key or signature' end - public_key = Gem::Security.get_public_key(key) - raise Gem::Security::Exception, "certificate #{signer.subject} does not match the signing key" unless - signer.public_key.to_pem == public_key.to_pem + signer.check_private_key(key) true end From 6819e3d0fadc10ce8d10919402eedb730cf0e43f Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Wed, 22 Dec 2021 01:54:10 +0900 Subject: [PATCH 3/3] Fix Gem::Security.get_public_key on OpenSSL 3.0 Ruby/OpenSSL 2.2 added OpenSSL::PKey::PKey#public_to_der for serializing only the public key components contained in the instance. This works for all possible key types. --- lib/rubygems/security.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb index 2906819bd34..f21c1756422 100644 --- a/lib/rubygems/security.rb +++ b/lib/rubygems/security.rb @@ -424,6 +424,8 @@ def self.create_cert(subject, key, age = ONE_YEAR, extensions = EXTENSIONS, # Gets the right public key from a PKey instance def self.get_public_key(key) + # Ruby 3.0 (Ruby/OpenSSL 2.2) or later + return OpenSSL::PKey.read(key.public_to_der) if key.respond_to?(:public_to_der) return key.public_key unless key.is_a?(OpenSSL::PKey::EC) ec_key = OpenSSL::PKey::EC.new(key.group.curve_name)