puppetlabs-puppet/ext/cert_inspector

141 строка
4.4 KiB
Ruby
Executable File

#!/usr/bin/env ruby
require 'openssl'
class X509Collector
include Enumerable
def initialize
@collected_data = {}
end
def interpret_contents(contents)
cls = case contents.split("\n")[0]
when /BEGIN X509 CRL/ then OpenSSL::X509::CRL
when /BEGIN CERTIFICATE REQUEST/ then OpenSSL::X509::Request
when /BEGIN CERTIFICATE/ then OpenSSL::X509::Certificate
when /BEGIN RSA (PRIVATE|PUBLIC) KEY/ then OpenSSL::PKey::RSA
else return nil
end
cls.new(contents)
rescue
nil
end
def expected_non_x509_files
['inventory.txt', 'ca.pass', 'serial']
end
def investigate_path(path)
if File.directory?(path)
Dir.foreach(path) do |x|
next if ['.', '..'].include? x
investigate_path File.join(path, x)
end
else
contents = File.read path
meaning = interpret_contents contents
unless meaning || expected_non_x509_files.include?(File.basename(path))
puts "WARNING: file #{path.inspect} could not be interpreted"
end
@collected_data[path] = meaning if meaning
end
end
def each(&block)
@collected_data.each(&block)
end
def extract_public_key_info(path, meaning)
case meaning
when OpenSSL::PKey::RSA
if meaning.private?
[meaning.public_key, 2, path]
else
[meaning, 3, path]
end
when OpenSSL::X509::Certificate
[meaning.public_key, 0, meaning.subject.to_s]
when OpenSSL::X509::Request
[meaning.public_key, 1, meaning.subject.to_s]
end
end
def who_signed(meaning, key_names, keys)
signing_key = keys.find { |key| meaning.verify(key) }
if signing_key then "#{key_names[signing_key.to_s]}" else "???" end
end
def explain(meaning, key_names, keys)
case meaning
when OpenSSL::PKey::RSA
if meaning.private?
"Private key for #{key_names[meaning.public_key.to_s]}"
else
"Public key for #{key_names[meaning.public_key.to_s]}"
end
when OpenSSL::X509::Certificate
signature_desc = who_signed(meaning, key_names, keys)
"Certificate assigning name #{meaning.subject.to_s} to #{key_names[meaning.public_key.to_s]}\n serial number #{meaning.serial}\n issued by #{meaning.issuer.to_s}\n signed by #{signature_desc}"
when OpenSSL::X509::Request
signature_desc = who_signed(meaning, key_names, keys)
"Certificate request for #{meaning.subject.to_s} having key #{key_names[meaning.public_key.to_s]}\n signed by #{signature_desc}"
when OpenSSL::X509::CRL
signature_desc = who_signed(meaning, key_names, keys)
revoked_serial_numbers = meaning.revoked.map { |r| r.serial }
revoked_desc = if revoked_serial_numbers.count > 0 then "serial numbers #{revoked_serial_numbers.inspect}" else "nothing" end
"Certificate revocation list revoking #{revoked_desc}\n issued by #{meaning.issuer.to_s}\n signed by #{signature_desc}"
else
"Unknown"
end
end
# Yield unique public keys, with a canonical name for each.
def collect_public_keys
key_data = {} # pem => (priority, name, public_key)
@collected_data.collect do |path, meaning|
begin
next unless public_key_info = extract_public_key_info(path, meaning)
public_key, priority, name = public_key_info
pem = public_key.to_s
existing_priority, existing_name, existing_public_key = key_data[pem]
next if existing_priority and existing_priority < priority
key_data[pem] = priority, name, public_key
rescue
puts "exception!"
end
end
name_to_key_hash = {}
key_data.each do |pem, data|
priority, name, public_key = data
if name_to_key_hash[name]
suffix_num = 2
while name_to_key_hash[name + " (#{suffix_num})"]
suffix_num += 1
end
name = name + " (#{suffix_num})"
end
name_to_key_hash[name] = public_key
end
key_names = {}
keys = []
name_to_key_hash.each do |name, public_key|
key_names[public_key.to_s] = "key<#{name}>"
keys << public_key
end
[key_names, keys]
end
end
collector = X509Collector.new
ARGV.each do |path|
collector.investigate_path(path)
end
key_names, keys = collector.collect_public_keys
collector.map do |path, meaning|
[collector.explain(meaning, key_names, keys), path]
end.sort.each do |description, path|
puts "#{path}:"
puts " #{description}"
puts
end