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.
Create a new Gem::Security::Policy object with the given mode and options.
# File 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
Ensures that signer is valid for time and was
signed by the issuer. If the issuer is
nil no verification is performed.
# File 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 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 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 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 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 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 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 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
Commenting is here to help enhance the documentation. For example, code samples, or clarification of the documentation.
If you have questions about Ruby or the documentation, please post to one of the Ruby mailing lists. You will get better, faster, help that way.
If you wish to post a correction of the docs, please do so, but also file bug report so that it can be corrected for the next release. Thank you.
If you want to help improve the Ruby documentation, please see Improve the docs, or visit Documenting-ruby.org.