class Gem::Security::Policy
A Gem::Security::Policy object encapsulates the settings for verifying signed gem files. This is the base class. You can either declare an instance of this or use one of the preset security policies in Gem::Security::Policies.
Attributes
Public Class Methods
Create a new Gem::Security::Policy object with the given mode and options.
# File lib/rubygems/security/policy.rb, line 22 def initialize name, policy = {}, opt = {} require 'openssl' @name = name @opt = opt # Default to security @only_signed = true @only_trusted = true @verify_chain = true @verify_data = true @verify_root = true @verify_signer = true policy.each_pair do |key, val| case key when :verify_data then @verify_data = val when :verify_signer then @verify_signer = val when :verify_chain then @verify_chain = val when :verify_root then @verify_root = val when :only_trusted then @only_trusted = val when :only_signed then @only_signed = val end end end
Public Instance Methods
Ensures that signer
is valid for time
and was
signed by the issuer
. If the issuer
is
nil
no verification is performed.
# File lib/rubygems/security/policy.rb, line 83 def check_cert signer, issuer, time raise Gem::Security::Exception, 'missing signing certificate' unless signer message = "certificate #{signer.subject}" if not_before = signer.not_before and not_before > time then raise Gem::Security::Exception, "#{message} not valid before #{not_before}" end if not_after = signer.not_after and not_after < time then raise Gem::Security::Exception, "#{message} not valid after #{not_after}" end if issuer and not signer.verify issuer.public_key then raise Gem::Security::Exception, "#{message} was not issued by #{issuer.subject}" end true end
Verifies each certificate in chain
has signed the following
certificate and is valid for the given time
.
# File lib/rubygems/security/policy.rb, line 53 def check_chain chain, time raise Gem::Security::Exception, 'missing signing chain' unless chain raise Gem::Security::Exception, 'empty signing chain' if chain.empty? begin chain.each_cons 2 do |issuer, cert| check_cert cert, issuer, time end true rescue Gem::Security::Exception => e raise Gem::Security::Exception, "invalid signing chain: #{e.message}" end end
Verifies that data
matches the signature
created
by public_key
and the digest
algorithm.
# File lib/rubygems/security/policy.rb, line 72 def check_data public_key, digest, signature, data raise Gem::Security::Exception, "invalid signature" unless public_key.verify digest.new, signature, data.digest true end
Ensures the public key of key
matches the public key in
signer
# File lib/rubygems/security/policy.rb, line 109 def check_key signer, key unless signer and key then return true unless @only_signed raise Gem::Security::Exception, 'missing key or signature' end raise Gem::Security::Exception, "certificate #{signer.subject} does not match the signing key" unless signer.public_key.to_pem == key.public_key.to_pem true end
Ensures the root certificate in chain
is self-signed and valid
for time
.
# File lib/rubygems/security/policy.rb, line 127 def check_root chain, time raise Gem::Security::Exception, 'missing signing chain' unless chain root = chain.first raise Gem::Security::Exception, 'missing root certificate' unless root raise Gem::Security::Exception, "root certificate #{root.subject} is not self-signed " + "(issuer #{root.issuer})" if root.issuer.to_s != root.subject.to_s # HACK to_s is for ruby 1.8 check_cert root, root, time end
Ensures the root of chain
has a trusted certificate in
trust_dir
and the digests of the two certificates match
according to digester
# File lib/rubygems/security/policy.rb, line 146 def check_trust chain, digester, trust_dir raise Gem::Security::Exception, 'missing signing chain' unless chain root = chain.first raise Gem::Security::Exception, 'missing root certificate' unless root path = Gem::Security.trust_dir.cert_path root unless File.exist? path then message = "root cert #{root.subject} is not trusted" message << " (root of signing cert #{chain.last.subject})" if chain.length > 1 raise Gem::Security::Exception, message end save_cert = OpenSSL::X509::Certificate.new File.read path save_dgst = digester.digest save_cert.public_key.to_s pkey_str = root.public_key.to_s cert_dgst = digester.digest pkey_str raise Gem::Security::Exception, "trusted root certificate #{root.subject} checksum " + "does not match signing root certificate checksum" unless save_dgst == cert_dgst true end
Verifies the certificate chain
is valid, the
digests
match the signatures signatures
created
by the signer depending on the policy
settings.
If key
is given it is used to validate the signing
certificate.
# File lib/rubygems/security/policy.rb, line 193 def verify chain, key = nil, digests = {}, signatures = {} if @only_signed and signatures.empty? then raise Gem::Security::Exception, "unsigned gems are not allowed by the #{name} policy" end opt = @opt digester = Gem::Security::DIGEST_ALGORITHM trust_dir = opt[:trust_dir] time = Time.now _, signer_digests = digests.find do |algorithm, file_digests| file_digests.values.first.name == Gem::Security::DIGEST_NAME end if @verify_data then raise Gem::Security::Exception, 'no digests provided (probable bug)' if signer_digests.nil? or signer_digests.empty? else signer_digests = {} end signer = chain.last check_key signer, key if key check_cert signer, nil, time if @verify_signer check_chain chain, time if @verify_chain check_root chain, time if @verify_root check_trust chain, digester, trust_dir if @only_trusted signatures.each do |file, _| digest = signer_digests[file] raise Gem::Security::Exception, "missing digest for #{file}" unless digest end signer_digests.each do |file, digest| signature = signatures[file] raise Gem::Security::Exception, "missing signature for #{file}" unless signature check_data signer.public_key, digester, signature, digest if @verify_data end true end
Extracts the certificate chain from the spec
and calls verify to ensure the signatures and
certificate chain is valid according to the policy..
# File lib/rubygems/security/policy.rb, line 250 def verify_signatures spec, digests, signatures chain = spec.cert_chain.map do |cert_pem| OpenSSL::X509::Certificate.new cert_pem end verify chain, nil, digests, signatures true end