forked from puppetlabs/puppet
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcert_inspector
executable file
·140 lines (129 loc) · 4.38 KB
/
cert_inspector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/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