Class: Gem::Security::Signer
- Inherits:
-
Object
- Object
- Gem::Security::Signer
- Includes:
- UserInteraction
- Defined in:
- lib/rubygems/security/signer.rb
Constant Summary collapse
- DEFAULT_OPTIONS =
{ expiration_length_days: 365, }.freeze
Instance Attribute Summary collapse
-
#cert_chain ⇒ Object
The chain of certificates for signing including the signing certificate.
-
#digest_algorithm ⇒ Object
readonly
The digest algorithm used to create the signature.
-
#digest_name ⇒ Object
readonly
The name of the digest algorithm, used to pull digests out of the hash by name.
-
#key ⇒ Object
The private key for the signing certificate.
-
#options ⇒ Object
readonly
Gem::Security::Signer options.
Class Method Summary collapse
-
.re_sign_cert(expired_cert, expired_cert_path, private_key) {|expired_cert_path, new_expired_cert_path| ... } ⇒ Object
Attempts to re-sign an expired cert with a given private key.
Instance Method Summary collapse
-
#extract_name(cert) ⇒ Object
Extracts the full name of
cert
. -
#initialize(key, cert_chain, passphrase = nil, options = {}) ⇒ Signer
constructor
Creates a new signer with an RSA
key
or path to a key, and a certificatechain
containing X509 certificates, encoding certificates or paths to certificates. -
#load_cert_chain ⇒ Object
Loads any missing issuers in the cert chain from the trusted certificates.
-
#re_sign_key(expiration_length: Gem::Security::ONE_YEAR) ⇒ Object
Attempts to re-sign the private key if the signing certificate is expired.
-
#sign(data) ⇒ Object
Sign data with given digest algorithm.
Methods included from UserInteraction
#alert, #alert_error, #alert_warning, #ask, #ask_for_password, #ask_yes_no, #choose_from_list, #say, #terminate_interaction, #verbose
Methods included from DefaultUserInteraction
ui, #ui, ui=, #ui=, use_ui, #use_ui
Methods included from Text
#clean_text, #format_text, #levenshtein_distance, #min3, #truncate_text
Constructor Details
#initialize(key, cert_chain, passphrase = nil, options = {}) ⇒ Signer
Creates a new signer with an RSA key
or path to a key, and a certificate chain
containing X509 certificates, encoding certificates or paths to certificates.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/rubygems/security/signer.rb', line 67 def initialize(key, cert_chain, passphrase = nil, = {}) @cert_chain = cert_chain @key = key @passphrase = passphrase @options = DEFAULT_OPTIONS.merge() unless @key default_key = File.join Gem.default_key_path @key = default_key if File.exist? default_key end unless @cert_chain default_cert = File.join Gem.default_cert_path @cert_chain = [default_cert] if File.exist? default_cert end @digest_name = Gem::Security::DIGEST_NAME @digest_algorithm = Gem::Security.create_digest(@digest_name) if @key && !@key.is_a?(OpenSSL::PKey::RSA) @key = OpenSSL::PKey::RSA.new(File.read(@key), @passphrase) end if @cert_chain @cert_chain = @cert_chain.compact.map do |cert| next cert if OpenSSL::X509::Certificate === cert cert = File.read cert if File.exist? cert OpenSSL::X509::Certificate.new cert end load_cert_chain end end |
Instance Attribute Details
#cert_chain ⇒ Object
The chain of certificates for signing including the signing certificate
13 14 15 |
# File 'lib/rubygems/security/signer.rb', line 13 def cert_chain @cert_chain end |
#digest_algorithm ⇒ Object (readonly)
The digest algorithm used to create the signature
23 24 25 |
# File 'lib/rubygems/security/signer.rb', line 23 def digest_algorithm @digest_algorithm end |
#digest_name ⇒ Object (readonly)
The name of the digest algorithm, used to pull digests out of the hash by name.
29 30 31 |
# File 'lib/rubygems/security/signer.rb', line 29 def digest_name @digest_name end |
#key ⇒ Object
The private key for the signing certificate
18 19 20 |
# File 'lib/rubygems/security/signer.rb', line 18 def key @key end |
#options ⇒ Object (readonly)
Gem::Security::Signer options
34 35 36 |
# File 'lib/rubygems/security/signer.rb', line 34 def @options end |
Class Method Details
.re_sign_cert(expired_cert, expired_cert_path, private_key) {|expired_cert_path, new_expired_cert_path| ... } ⇒ Object
Attempts to re-sign an expired cert with a given private key
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/rubygems/security/signer.rb', line 42 def self.re_sign_cert(expired_cert, expired_cert_path, private_key) return unless expired_cert.not_after < Time.now expiry = expired_cert.not_after.strftime('%Y%m%d%H%M%S') expired_cert_file = "#{File.basename(expired_cert_path)}.expired.#{expiry}" new_expired_cert_path = File.join(Gem.user_home, ".gem", expired_cert_file) Gem::Security.write(expired_cert, new_expired_cert_path) re_signed_cert = Gem::Security.re_sign( expired_cert, private_key, (Gem::Security::ONE_DAY * Gem.configuration.cert_expiration_length_days) ) Gem::Security.write(re_signed_cert, expired_cert_path) yield(expired_cert_path, new_expired_cert_path) if block_given? end |
Instance Method Details
#extract_name(cert) ⇒ Object
Extracts the full name of cert
. If the certificate has a subjectAltName this value is preferred, otherwise the subject is used.
107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/rubygems/security/signer.rb', line 107 def extract_name(cert) # :nodoc: subject_alt_name = cert.extensions.find {|e| 'subjectAltName' == e.oid } if subject_alt_name /\Aemail:/ =~ subject_alt_name.value # rubocop:disable Performance/StartWith $' || subject_alt_name.value else cert.subject end end |
#load_cert_chain ⇒ Object
Loads any missing issuers in the cert chain from the trusted certificates.
If the issuer does not exist it is ignored as it will be checked later.
124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/rubygems/security/signer.rb', line 124 def load_cert_chain # :nodoc: return if @cert_chain.empty? while @cert_chain.first.issuer.to_s != @cert_chain.first.subject.to_s do issuer = Gem::Security.trust_dir.issuer_of @cert_chain.first break unless issuer # cert chain is verified later @cert_chain.unshift issuer end end |
#re_sign_key(expiration_length: Gem::Security::ONE_YEAR) ⇒ Object
Attempts to re-sign the private key if the signing certificate is expired.
The key will be re-signed if:
-
The expired certificate is self-signed
-
The expired certificate is saved at ~/.gem/gem-public_cert.pem and the private key is saved at ~/.gem/gem-private_key.pem
-
There is no file matching the expiry date at ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S
If the signing certificate can be re-signed the expired certificate will be saved as ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S where the expiry time (not after) is used for the timestamp.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/rubygems/security/signer.rb', line 173 def re_sign_key(expiration_length: Gem::Security::ONE_YEAR) # :nodoc: old_cert = @cert_chain.last disk_cert_path = File.join(Gem.default_cert_path) disk_cert = File.read(disk_cert_path) rescue nil disk_key_path = File.join(Gem.default_key_path) disk_key = OpenSSL::PKey::RSA.new(File.read(disk_key_path), @passphrase) rescue nil return unless disk_key if disk_key.to_pem == @key.to_pem && disk_cert == old_cert.to_pem expiry = old_cert.not_after.strftime('%Y%m%d%H%M%S') old_cert_file = "gem-public_cert.pem.expired.#{expiry}" old_cert_path = File.join(Gem.user_home, ".gem", old_cert_file) unless File.exist?(old_cert_path) Gem::Security.write(old_cert, old_cert_path) cert = Gem::Security.re_sign(old_cert, @key, expiration_length) Gem::Security.write(cert, disk_cert_path) alert("Your cert: #{disk_cert_path} has been auto re-signed with the key: #{disk_key_path}") alert("Your expired cert will be located at: #{old_cert_path}") @cert_chain = [cert] end end end |
#sign(data) ⇒ Object
Sign data with given digest algorithm
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/rubygems/security/signer.rb', line 139 def sign(data) return unless @key raise Gem::Security::Exception, 'no certs provided' if @cert_chain.empty? if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now alert("Your certificate has expired, trying to re-sign it...") re_sign_key( expiration_length: (Gem::Security::ONE_DAY * [:expiration_length_days]) ) end full_name = extract_name @cert_chain.last Gem::Security::SigningPolicy.verify @cert_chain, @key, {}, {}, full_name @key.sign @digest_algorithm.new, data end |