summaryrefslogtreecommitdiff
path: root/sample/openssl/certstore.rb
blob: 72e59f6dad0077f2eddb7b48876a5a1f2f5cd4d8 (plain)
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
require 'c_rehash'
require 'crlstore'


class CertStore
  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 = OpenSSL::X509::Store.new
    @self_signed_ca = @other_ca = @ee = @crl = nil

    # Uncomment this line to let OpenSSL to check CRL for each certs.
    # @x509store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK | OpenSSL::X509::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