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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
require 'c_rehash'
require 'crlstore'
class CertStore
include OpenSSL
include X509
attr_reader :self_signed_ca
attr_reader :other_ca
attr_reader :ee
attr_reader :crl
attr_reader :request
def initialize(certs_dir)
@certs_dir = certs_dir
@c_store = CHashDir.new(@certs_dir)
@c_store.hash_dir(true)
@crl_store = CrlStore.new(@c_store)
@x509store = Store.new
@self_signed_ca = @other_ca = @ee = @crl = nil
# Uncomment this line to let OpenSSL to check CRL for each certs.
# @x509store.flags = V_FLAG_CRL_CHECK | V_FLAG_CRL_CHECK_ALL
add_path
scan_certs
end
def generate_cert(filename)
@c_store.load_pem_file(filename)
end
def verify(cert)
error, crl_map = do_verify(cert)
if error
[[false, cert, crl_map[cert.subject], error]]
else
@x509store.chain.collect { |c| [true, c, crl_map[c.subject], nil] }
end
end
def match_cert(cert1, cert2)
(cert1.issuer.cmp(cert2.issuer) == 0) and cert1.serial == cert2.serial
end
def is_ca?(cert)
case guess_cert_type(cert)
when CERT_TYPE_SELF_SIGNED
true
when CERT_TYPE_OTHER
true
else
false
end
end
def scan_certs
@self_signed_ca = []
@other_ca = []
@ee = []
@crl = []
@request = []
load_certs
end
private
def add_path
@x509store.add_path(@certs_dir)
end
def do_verify(cert)
error_map = {}
crl_map = {}
result = @x509store.verify(cert) do |ok, ctx|
cert = ctx.current_cert
if ctx.current_crl
crl_map[cert.subject] = true
end
if ok
if !ctx.current_crl
if crl = @crl_store.find_crl(cert)
crl_map[cert.subject] = true
if crl.revoked.find { |revoked| revoked.serial == cert.serial }
ok = false
error_string = 'certification revoked'
end
end
end
end
error_map[cert.subject] = error_string if error_string
ok
end
error = if result
nil
else
error_map[cert.subject] || @x509store.error_string
end
return error, crl_map
end
def load_certs
@c_store.get_certs.each do |certfile|
cert = generate_cert(certfile)
case guess_cert_type(cert)
when CERT_TYPE_SELF_SIGNED
@self_signed_ca << cert
when CERT_TYPE_OTHER
@other_ca << cert
when CERT_TYPE_EE
@ee << cert
else
raise "Unknown cert type."
end
end
@c_store.get_crls.each do |crlfile|
@crl << generate_cert(crlfile)
end
end
CERT_TYPE_SELF_SIGNED = 0
CERT_TYPE_OTHER = 1
CERT_TYPE_EE = 2
def guess_cert_type(cert)
ca = self_signed = is_cert_self_signed(cert)
cert.extensions.each do |ext|
# Ignores criticality of extensions. It's 'guess'ing.
case ext.oid
when 'basicConstraints'
/CA:(TRUE|FALSE), pathlen:(\d+)/ =~ ext.value
ca = ($1 == 'TRUE') unless ca
when 'keyUsage'
usage = ext.value.split(/\s*,\s*/)
ca = usage.include?('Certificate Sign') unless ca
when 'nsCertType'
usage = ext.value.split(/\s*,\s*/)
ca = usage.include?('SSL CA') unless ca
end
end
if ca
if self_signed
CERT_TYPE_SELF_SIGNED
else
CERT_TYPE_OTHER
end
else
CERT_TYPE_EE
end
end
def is_cert_self_signed(cert)
# cert.subject.cmp(cert.issuer) == 0
cert.subject.to_s == cert.issuer.to_s
end
end
if $0 == __FILE__
c = CertStore.new("trust_certs")
end
|