class Gem::Security::Policy

Gem::Security::Policy 对象封装了验证签名 gem 文件的设置。这是基类。你可以声明此类的实例,或者使用 Gem::Security::Policies 中预设的安全策略之一。

属性

name[R]
only_signed[RW]
only_trusted[RW]
to_s[R]
verify_chain[RW]
verify_data[RW]
verify_root[RW]
verify_signer[RW]

公共类方法

new(name, policy = {}, opt = {}) 点击切换源代码

使用给定的模式和选项创建一个新的 Gem::Security::Policy 对象。

# File rubygems/security/policy.rb, line 27
def initialize(name, policy = {}, opt = {})
  @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

公共实例方法

check_cert(signer, issuer, time) 点击切换源代码

确保 signer 对于 time 有效,并且由 issuer 签名。如果 issuernil,则不执行验证。

# File rubygems/security/policy.rb, line 86
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) && not_before > time
    raise Gem::Security::Exception,
          "#{message} not valid before #{not_before}"
  end

  if (not_after = signer.not_after) && not_after < time
    raise Gem::Security::Exception, "#{message} not valid after #{not_after}"
  end

  if issuer && !signer.verify(issuer.public_key)
    raise Gem::Security::Exception,
          "#{message} was not issued by #{issuer.subject}"
  end

  true
end
check_chain(chain, time) 点击切换源代码

验证 chain 中的每个证书都已签名后续证书,并且在给定的 time 内有效。

# File rubygems/security/policy.rb, line 56
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
check_data(public_key, digest, signature, data) 点击切换源代码

验证 data 是否与 public_keydigest 算法创建的 signature 匹配。

# File rubygems/security/policy.rb, line 75
def check_data(public_key, digest, signature, data)
  raise Gem::Security::Exception, "invalid signature" unless
    public_key.verify digest, signature, data.digest

  true
end
check_key(signer, key) 点击切换源代码

确保 key 的公钥与 signer 中的公钥匹配

# File rubygems/security/policy.rb, line 112
def check_key(signer, key)
  unless signer && key
    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.check_private_key(key)

  true
end
check_root(chain, time) 点击切换源代码

确保 chain 中的根证书是自签名且在 time 内有效的。

# File rubygems/security/policy.rb, line 130
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 != root.subject

  check_cert root, root, time
end
check_trust(chain, digester, trust_dir) 点击切换源代码

确保 chain 的根证书在 trust_dir 中具有受信任的证书,并且两个证书的摘要根据 digester 匹配

# File rubygems/security/policy.rb, line 149
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
    message = "root cert #{root.subject} is not trusted".dup

    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_pem

  pkey_str = root.public_key.to_pem
  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
verify(chain, key = nil, digests = {}, signatures = {}, full_name = "(unknown)") 点击切换源代码

对于 full_name,验证证书 chain 是否有效,digests 是否与签名者根据 policy 设置创建的签名 signatures 匹配。

如果给定了 key,则使用它来验证签名证书。

# File rubygems/security/policy.rb, line 206
def verify(chain, key = nil, digests = {}, signatures = {}, full_name = "(unknown)")
  if signatures.empty?
    if @only_signed
      raise Gem::Security::Exception,
        "unsigned gems are not allowed by the #{name} policy"
    elsif digests.empty?
      # lack of signatures is irrelevant if there is nothing to check
      # against
    else
      alert_warning "#{full_name} is not signed"
      return
    end
  end

  opt       = @opt
  digester  = Gem::Security.create_digest
  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
    raise Gem::Security::Exception, "no digests provided (probable bug)" if
      signer_digests.nil? || 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

  if @only_trusted
    check_trust chain, digester, trust_dir
  elsif signatures.empty? && digests.empty?
    # trust is irrelevant if there's no signatures to verify
  else
    alert_warning "#{subject signer} is not trusted for #{full_name}"
  end

  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
verify_signatures(spec, digests, signatures) 点击切换源代码

spec 中提取证书链,并调用 verify 以确保签名和证书链根据策略有效。

# File rubygems/security/policy.rb, line 277
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, spec.full_name

  true
end