Class: Gem::Security::Signer

Inherits:
Object
  • Object
show all
Defined in:
lib/rubygems/security/signer.rb

Overview

Basic OpenSSL-based package signing class.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, cert_chain) ⇒ 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.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/rubygems/security/signer.rb', line 32

def initialize key, cert_chain
  @cert_chain = cert_chain
  @key        = key

  unless @key then
    default_key  = File.join Gem.user_home, 'gem-private_key.pem'
    @key = default_key if File.exist? default_key
  end

  unless @cert_chain then
    default_cert = File.join Gem.user_home, 'gem-public_cert.pem'
    @cert_chain = [default_cert] if File.exist? default_cert
  end

  @digest_algorithm = Gem::Security::DIGEST_ALGORITHM
  @digest_name      = Gem::Security::DIGEST_NAME

  @key = OpenSSL::PKey::RSA.new File.read @key if
    @key and not OpenSSL::PKey::RSA === @key

  if @cert_chain then
    @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_chainObject

The chain of certificates for signing including the signing certificate



9
10
11
# File 'lib/rubygems/security/signer.rb', line 9

def cert_chain
  @cert_chain
end

#digest_algorithmObject (readonly)

The digest algorithm used to create the signature



19
20
21
# File 'lib/rubygems/security/signer.rb', line 19

def digest_algorithm
  @digest_algorithm
end

#digest_nameObject (readonly)

The name of the digest algorithm, used to pull digests out of the hash by name.



25
26
27
# File 'lib/rubygems/security/signer.rb', line 25

def digest_name
  @digest_name
end

#keyObject

The private key for the signing certificate



14
15
16
# File 'lib/rubygems/security/signer.rb', line 14

def key
  @key
end

Instance Method Details

#load_cert_chainObject

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.



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/rubygems/security/signer.rb', line 70

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_keyObject

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

  • 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-pubilc_cert.pem.expired.%Y%m%d%H%M%S where the expiry time (not after) is used for the timestamp.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/rubygems/security/signer.rb', line 110

def re_sign_key # :nodoc:
  old_cert = @cert_chain.last

  disk_cert_path = File.join Gem.user_home, 'gem-public_cert.pem'
  disk_cert = File.read disk_cert_path rescue nil
  disk_key  =
    File.read File.join(Gem.user_home, 'gem-private_key.pem') rescue nil

  if disk_key == @key.to_pem and disk_cert == old_cert.to_pem then
    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, old_cert_file

    unless File.exist? old_cert_path then
      Gem::Security.write old_cert, old_cert_path

      cert = Gem::Security.re_sign old_cert, @key

      Gem::Security.write cert, disk_cert_path

      @cert_chain = [cert]
    end
  end
end

#sign(data) ⇒ Object

Sign data with given digest algorithm



85
86
87
88
89
90
91
92
93
94
95
# File 'lib/rubygems/security/signer.rb', line 85

def sign data
  return unless @key

  if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now then
    re_sign_key
  end

  Gem::Security::SigningPolicy.verify @cert_chain, @key

  @key.sign @digest_algorithm.new, data
end