1require 'c_rehash' 2require 'crlstore' 3 4 5class CertStore 6 include OpenSSL 7 include X509 8 9 attr_reader :self_signed_ca 10 attr_reader :other_ca 11 attr_reader :ee 12 attr_reader :crl 13 attr_reader :request 14 15 def initialize(certs_dir) 16 @certs_dir = certs_dir 17 @c_store = CHashDir.new(@certs_dir) 18 @c_store.hash_dir(true) 19 @crl_store = CrlStore.new(@c_store) 20 @x509store = Store.new 21 @self_signed_ca = @other_ca = @ee = @crl = nil 22 23 # Uncomment this line to let OpenSSL to check CRL for each certs. 24 # @x509store.flags = V_FLAG_CRL_CHECK | V_FLAG_CRL_CHECK_ALL 25 26 add_path 27 scan_certs 28 end 29 30 def generate_cert(filename) 31 @c_store.load_pem_file(filename) 32 end 33 34 def verify(cert) 35 error, crl_map = do_verify(cert) 36 if error 37 [[false, cert, crl_map[cert.subject], error]] 38 else 39 @x509store.chain.collect { |c| [true, c, crl_map[c.subject], nil] } 40 end 41 end 42 43 def match_cert(cert1, cert2) 44 (cert1.issuer.cmp(cert2.issuer) == 0) and cert1.serial == cert2.serial 45 end 46 47 def is_ca?(cert) 48 case guess_cert_type(cert) 49 when CERT_TYPE_SELF_SIGNED 50 true 51 when CERT_TYPE_OTHER 52 true 53 else 54 false 55 end 56 end 57 58 def scan_certs 59 @self_signed_ca = [] 60 @other_ca = [] 61 @ee = [] 62 @crl = [] 63 @request = [] 64 load_certs 65 end 66 67private 68 69 def add_path 70 @x509store.add_path(@certs_dir) 71 end 72 73 def do_verify(cert) 74 error_map = {} 75 crl_map = {} 76 result = @x509store.verify(cert) do |ok, ctx| 77 cert = ctx.current_cert 78 if ctx.current_crl 79 crl_map[cert.subject] = true 80 end 81 if ok 82 if !ctx.current_crl 83 if crl = @crl_store.find_crl(cert) 84 crl_map[cert.subject] = true 85 if crl.revoked.find { |revoked| revoked.serial == cert.serial } 86 ok = false 87 error_string = 'certification revoked' 88 end 89 end 90 end 91 end 92 error_map[cert.subject] = error_string if error_string 93 ok 94 end 95 error = if result 96 nil 97 else 98 error_map[cert.subject] || @x509store.error_string 99 end 100 return error, crl_map 101 end 102 103 def load_certs 104 @c_store.get_certs.each do |certfile| 105 cert = generate_cert(certfile) 106 case guess_cert_type(cert) 107 when CERT_TYPE_SELF_SIGNED 108 @self_signed_ca << cert 109 when CERT_TYPE_OTHER 110 @other_ca << cert 111 when CERT_TYPE_EE 112 @ee << cert 113 else 114 raise "Unknown cert type." 115 end 116 end 117 @c_store.get_crls.each do |crlfile| 118 @crl << generate_cert(crlfile) 119 end 120 end 121 122 CERT_TYPE_SELF_SIGNED = 0 123 CERT_TYPE_OTHER = 1 124 CERT_TYPE_EE = 2 125 def guess_cert_type(cert) 126 ca = self_signed = is_cert_self_signed(cert) 127 cert.extensions.each do |ext| 128 # Ignores criticality of extensions. It's 'guess'ing. 129 case ext.oid 130 when 'basicConstraints' 131 /CA:(TRUE|FALSE), pathlen:(\d+)/ =~ ext.value 132 ca = ($1 == 'TRUE') unless ca 133 when 'keyUsage' 134 usage = ext.value.split(/\s*,\s*/) 135 ca = usage.include?('Certificate Sign') unless ca 136 when 'nsCertType' 137 usage = ext.value.split(/\s*,\s*/) 138 ca = usage.include?('SSL CA') unless ca 139 end 140 end 141 if ca 142 if self_signed 143 CERT_TYPE_SELF_SIGNED 144 else 145 CERT_TYPE_OTHER 146 end 147 else 148 CERT_TYPE_EE 149 end 150 end 151 152 def is_cert_self_signed(cert) 153 # cert.subject.cmp(cert.issuer) == 0 154 cert.subject.to_s == cert.issuer.to_s 155 end 156end 157 158 159if $0 == __FILE__ 160 c = CertStore.new("trust_certs") 161end 162