class Gem::Security::Signer

Constants

DEFAULT_OPTIONS

Attributes

The chain of certificates for signing including the signing certificate

The digest algorithm used to create the signature

The private key for the signing certificate

Public Class Methods

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.

# File lib/rubygems/security/signer.rb, line 68
def initialize(key, cert_chain, passphrase = nil, options = {})
  @cert_chain = cert_chain
  @key        = key
  @passphrase = passphrase
  @options = DEFAULT_OPTIONS.merge(options)

  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::PKey)
    @key = OpenSSL::PKey.read(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

Attempts to re-sign an expired cert with a given private key

# File lib/rubygems/security/signer.rb, line 43
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

Public Instance Methods

Sign data with given digest algorithm

# File lib/rubygems/security/signer.rb, line 140
def sign(data)
  return unless @key

  raise Gem::Security::Exception, "no certs provided" if @cert_chain.empty?

  if @cert_chain.length == 1 && @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 * options[: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