1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* ext/openssl/sample: Add samples.

- cert2text.rb: Dump certificate file as text.
  - crlstore.rb: CRL store implementation.  Fetch CRL via HTTP when
    http-access2 is installed.
  - certstore.rb: Certificate store implementation.
  - cert_store_view.rb: Certificate store viewer with FXRuby.  Uses
    c_rehash.rb, crlstore.rb and certstore.rb.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4160 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nahi 2003-07-25 15:12:26 +00:00
parent b35031495a
commit ccdc8d302a
5 changed files with 1232 additions and 5 deletions

View file

@ -1,3 +1,13 @@
Fri Jul 25 15:04:25 2003 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* ext/openssl/sample: Add samples.
- cert2text.rb: Dump certificate file as text.
- crlstore.rb: CRL store implementation. Fetch CRL via HTTP when
http-access2 is installed.
- certstore.rb: Certificate store implementation.
- cert_store_view.rb: Certificate store viewer with FXRuby. Uses
c_rehash.rb, crlstore.rb and certstore.rb.
Fri Jul 25 15:47:39 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
* ext/openssl/extconf.rb: add check for BN_rand_range() and
@ -855,7 +865,7 @@ Thu Jun 19 13:13:10 2003 NAKAMURA Usaku <usa@ruby-lang.org>
* hash.c (env_delete, rb_f_getenv, env_fetch): case insensitive to
access environment variables on DOSISH platforms.
Thu Jun 19 00:51:47 2003 NAKAMURA Hiroshi <nakahiro@sarion.co.jp>
Thu Jun 19 00:51:47 2003 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* range.c (rb_range_beg_len): out_of_range check after adjusting
end point. [ruby-dev:20370]
@ -2996,7 +3006,7 @@ Wed Feb 26 17:38:16 2003 Tanaka Akira <akr@m17n.org>
* lib/open-uri.rb: replace Kernel.open as well.
Tue Feb 25 23:03:08 2003 NAKAMURA Hiroshi <nahi@ruby-lang.org>
Tue Feb 25 23:03:08 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
* lib/debug.rb (DEBUGGER__::Context#debug_command): bp filename must
be the basename of it. [ruby-talk:65644]
@ -4889,7 +4899,7 @@ Sun Nov 17 22:57:31 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* parse.y (dsym): garbage returned. (ruby-bugs-ja:PR#358)
Fri Nov 15 07:40:08 2002 NAKAMURA Hiroshi <nakahiro@sarion.co.jp>
Fri Nov 15 07:40:08 2002 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* observer.rb: raise NoMethodError instead of NameError.
[ruby-dev:18788]
@ -13961,7 +13971,7 @@ Fri Dec 29 00:38:46 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* m17n.c: new file - core functions of M17N.
Tue Dec 26 18:46:41 2000 NAKAMURA Hiroshi <nakahiro@sarion.co.jp>
Tue Dec 26 18:46:41 2000 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* lib/debug.rb: Avoid thread deadlock in debugging stopped thread.
@ -18516,7 +18526,7 @@ Fri Jul 16 01:37:50 1999 Koji Arai <JCA02266@nifty.ne.jp>
* string.c (rb_str_reverse): should always return copy.
Thu Jul 15 23:25:57 1999 NAKAMURA Hiroshi <nakahiro@sarion.co.jp>
Thu Jul 15 23:25:57 1999 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* lib/debug.rb: better display & frame treatment.

View file

@ -0,0 +1,23 @@
#!/usr/bin/env ruby
require 'openssl'
include OpenSSL::X509
def cert2text(cert_str)
[Certificate, CRL, Request].each do |klass|
begin
puts klass.new(cert_str).to_text
return
rescue
end
end
raise ArgumentError.new('Unknown format.')
end
if ARGV.empty?
cert2text(STDIN.read)
else
ARGV.each do |file|
cert2text(File.read(file))
end
end

View file

@ -0,0 +1,911 @@
#!/usr/bin/env ruby
require 'fox'
require 'openssl'
require 'time'
require 'certstore'
require 'getopts'
include Fox
module CertDumpSupport
def cert_label(cert)
subject_alt_name =
cert.extensions.find { |ext| ext.oid == 'subjectAltName' }
if subject_alt_name
subject_alt_name.value.split(/\s*,\s/).each do |alt_name_pair|
alt_tag, alt_name = alt_name_pair.split(/:/)
return alt_name
end
end
name_label(cert.subject)
end
def name_label(name)
ary = name.to_a
if (cn = ary.find { |rdn| rdn[0] == 'CN' })
return cn[1]
end
if ary.last[0] == 'OU'
return ary.last[1]
end
name.to_s
end
def name_text(name)
name.to_a.collect { |tag, value|
"#{tag} = #{value}"
}.reverse.join("\n")
end
def bn_label(bn)
("0" << sprintf("%X", bn)).scan(/../).join(" ")
end
end
class CertDump
include CertDumpSupport
def initialize(cert)
@cert = cert
end
def get_dump(tag)
case tag
when 'Version'
version
when 'Serial'
serial
when 'Signature Algorithm'
signature_algorithm
when 'Issuer'
issuer
when 'Validity'
validity
when 'Not before'
not_before
when 'Not after'
not_after
when 'Subject'
subject
when 'Public key'
public_key
else
ext(tag)
end
end
def get_dump_line(tag)
case tag
when 'Version'
version_line
when 'Serial'
serial_line
when 'Signature Algorithm'
signature_algorithm_line
when 'Subject'
subject_line
when 'Issuer'
issuer_line
when 'Validity'
validity_line
when 'Not before'
not_before_line
when 'Not after'
not_after_line
when 'Public key'
public_key_line
else
ext_line(tag)
end
end
private
def version
"Version: #{@cert.version + 1}"
end
def version_line
version
end
def serial
bn_label(@cert.serial)
end
def serial_line
serial
end
def signature_algorithm
@cert.signature_algorithm
end
def signature_algorithm_line
signature_algorithm
end
def subject
name_text(@cert.subject)
end
def subject_line
@cert.subject.to_s
end
def issuer
name_text(@cert.issuer)
end
def issuer_line
@cert.issuer.to_s
end
def validity
<<EOS
Not before: #{not_before}
Not after: #{not_after}
EOS
end
def validity_line
"from #{@cert.not_before.iso8601} to #{@cert.not_after.iso8601}"
end
def not_before
@cert.not_before.to_s
end
def not_before_line
not_before
end
def not_after
@cert.not_after.to_s
end
def not_after_line
not_after
end
def public_key
@cert.public_key.to_text
end
def public_key_line
"#{@cert.public_key.class} -- " << public_key.scan(/\A[^\n]*/)[0] << '...'
end
def ext(tag)
@cert.extensions.each do |ext|
if ext.oid == tag
return ext_detail(tag, ext.value)
end
end
"(unknown)"
end
def ext_line(tag)
ext(tag).tr("\r\n", '')
end
def ext_detail(tag, value)
value
end
end
class CrlDump
include CertDumpSupport
def initialize(crl)
@crl = crl
end
def get_dump(tag)
case tag
when 'Version'
version
when 'Signature Algorithm'
signature_algorithm
when 'Issuer'
issuer
when 'Last update'
last_update
when 'Next update'
next_update
else
ext(tag)
end
end
def get_dump_line(tag)
case tag
when 'Version'
version_line
when 'Signature Algorithm'
signature_algorithm_line
when 'Issuer'
issuer_line
when 'Last update'
last_update_line
when 'Next update'
next_update_line
else
ext_line(tag)
end
end
private
def version
"Version: #{@crl.version + 1}"
end
def version_line
version
end
def signature_algorithm
@crl.signature_algorithm
end
def signature_algorithm_line
signature_algorithm
end
def issuer
name_text(@crl.issuer)
end
def issuer_line
@crl.issuer.to_s
end
def last_update
@crl.last_update.to_s
end
def last_update_line
last_update
end
def next_update
@crl.next_update.to_s
end
def next_update_line
next_update
end
def ext(tag)
@crl.extensions.each do |ext|
if ext.oid == tag
return ext_detail(tag, ext.value)
end
end
"(unknown)"
end
def ext_line(tag)
ext(tag).tr("\r\n", '')
end
def ext_detail(tag, value)
value
end
end
class RevokedDump
include CertDumpSupport
def initialize(revoked)
@revoked = revoked
end
def get_dump(tag)
case tag
when 'Serial'
serial
when 'Time'
time
else
ext(tag)
end
end
def get_dump_line(tag)
case tag
when 'Serial'
serial_line
when 'Time'
time_line
else
ext_line(tag)
end
end
private
def serial
bn_label(@revoked.serial)
end
def serial_line
serial
end
def time
@revoked.time.to_s
end
def time_line
time
end
def ext(tag)
@revoked.extensions.each do |ext|
if ext.oid == tag
return ext_detail(tag, ext.value)
end
end
"(unknown)"
end
def ext_line(tag)
ext(tag).tr("\r\n", '')
end
def ext_detail(tag, value)
value
end
end
class RequestDump
include CertDumpSupport
def initialize(req)
@req = req
end
def get_dump(tag)
case tag
when 'Version'
version
when 'Signature Algorithm'
signature_algorithm
when 'Subject'
subject
when 'Public key'
public_key
else
attributes(tag)
end
end
def get_dump_line(tag)
case tag
when 'Version'
version_line
when 'Signature Algorithm'
signature_algorithm_line
when 'Subject'
subject_line
when 'Public key'
public_key_line
else
attributes_line(tag)
end
end
private
def version
"Version: #{@req.version + 1}"
end
def version_line
version
end
def signature_algorithm
@req.signature_algorithm
end
def signature_algorithm_line
signature_algorithm
end
def subject
name_text(@req.subject)
end
def subject_line
@req.subject.to_s
end
def public_key
@req.public_key.to_text
end
def public_key_line
"#{@req.public_key.class} -- " << public_key.scan(/\A[^\n]*/)[0] << '...'
end
def attributes(tag)
"(unknown)"
end
def attributes_line(tag)
attributes(tag).tr("\r\n", '')
end
end
class CertStoreView < FXMainWindow
class CertTree
include CertDumpSupport
def initialize(observer, tree)
@observer = observer
@tree = tree
@tree.connect(SEL_COMMAND) do |sender, sel, item|
if item.data
@observer.getApp().beginWaitCursor do
@observer.show_item(item.data)
end
else
@observer.show_item(nil)
end
end
end
def show(cert_store)
@tree.clearItems
@self_signed_ca_node = add_item_last(nil, "Trusted root CA")
@other_ca_node = add_item_last(nil, "Intermediate CA")
@ee_node = add_item_last(nil, "Personal")
@crl_node = add_item_last(nil, "CRL")
@request_node = add_item_last(nil, "Request")
@verify_path_node = add_item_last(nil, "Certification path")
show_certs(cert_store)
end
def show_certs(cert_store)
remove_items(@self_signed_ca_node)
remove_items(@other_ca_node)
remove_items(@ee_node)
remove_items(@crl_node)
remove_items(@request_node)
import_certs(cert_store)
end
def show_request(req)
node = add_item_last(@request_node, name_label(req.subject), req)
@tree.selectItem(node)
@observer.show_item(req)
end
def show_verify_path(verify_path)
add_verify_path(verify_path)
end
private
def open_node(node)
node.expanded = node.opened = true
end
def close_node(node)
node.expanded = node.opened = false
end
def import_certs(cert_store)
cert_store.self_signed_ca.each do |cert|
add_item_last(@self_signed_ca_node, cert_label(cert), cert)
end
cert_store.other_ca.each do |cert|
add_item_last(@other_ca_node, cert_label(cert), cert)
end
cert_store.ee.each do |cert|
add_item_last(@ee_node, cert_label(cert), cert)
end
cert_store.crl.each do |crl|
node = add_item_last(@crl_node, name_label(crl.issuer), crl)
close_node(node)
crl.revoked.each do |revoked|
add_item_last(node, bn_label(revoked.serial), revoked)
end
end
cert_store.request.each do |req|
add_item_last(@requestnode, name_label(req.subject), req)
end
end
def add_verify_path(verify_path)
node = @verify_path_node
last_cert = nil
verify_path.reverse_each do |ok, cert, crl_check, error_string|
warn = []
if @observer.cert_store.is_ca?(cert)
warn << 'NO ARL' unless crl_check
else
warn << 'NO CRL' unless crl_check
end
warn_str = '(' << warn.join(", ") << ')'
warn_mark = warn.empty? ? '' : '!'
label = if ok
"OK#{warn_mark}..." + cert_label(cert)
else
"NG(#{error_string})..." + cert_label(cert)
end
label << warn_str unless warn.empty?
node = add_item_last(node, label, cert)
node.expanded = true
last_cert = cert
end
if last_cert
@tree.selectItem(node)
@observer.show_item(last_cert)
end
end
def add_item_last(parent, label, obj = nil)
node = @tree.addItemLast(parent, FXTreeItem.new(label))
node.data = obj if obj
open_node(node)
node
end
def remove_items(node)
while node.getNumChildren > 0
@tree.removeItem(node.getFirst)
end
end
end
class CertInfo
def initialize(observer, table)
@observer = observer
@table = table
@table.leadingRows = 0
@table.leadingCols = 0
@table.trailingRows = 0
@table.trailingCols = 0
@table.showVertGrid(false)
@table.showHorzGrid(false)
@table.setTableSize(1, 2)
@table.setColumnWidth(0, 125)
@table.setColumnWidth(1, 350)
end
def show(item)
@observer.show_detail(nil, nil)
if item.nil?
set_column_size(1)
return
end
case item
when OpenSSL::X509::Certificate
show_cert(item)
when OpenSSL::X509::CRL
show_crl(item)
when OpenSSL::X509::Revoked
show_revoked(item)
when OpenSSL::X509::Request
show_request(item)
else
raise NotImplementedError.new("Unknown item type #{item.class}.")
end
end
private
def show_cert(cert)
wrap = CertDump.new(cert)
items = []
items << ['Version', wrap.get_dump_line('Version')]
items << ['Signature Algorithm', wrap.get_dump_line('Signature Algorithm')]
items << ['Issuer', wrap.get_dump_line('Issuer')]
items << ['Serial', wrap.get_dump_line('Serial')]
#items << ['Not before', wrap.get_dump_line('Not before')]
#items << ['Not after', wrap.get_dump_line('Not after')]
items << ['Subject', wrap.get_dump_line('Subject')]
items << ['Public key', wrap.get_dump_line('Public key')]
items << ['Validity', wrap.get_dump_line('Validity')]
(cert.extensions.sort { |a, b| a.oid <=> b.oid }).each do |ext|
items << [ext.oid, wrap.get_dump_line(ext.oid)]
end
show_items(cert, items)
end
def show_crl(crl)
wrap = CrlDump.new(crl)
items = []
items << ['Version', wrap.get_dump_line('Version')]
items << ['Signature Algorithm', wrap.get_dump_line('Signature Algorithm')]
items << ['Issuer', wrap.get_dump_line('Issuer')]
items << ['Last update', wrap.get_dump_line('Last update')]
items << ['Next update', wrap.get_dump_line('Next update')]
crl.extensions.each do |ext|
items << [ext.oid, wrap.get_dump_line(ext.oid)]
end
show_items(crl, items)
end
def show_revoked(revoked)
wrap = RevokedDump.new(revoked)
items = []
items << ['Serial', wrap.get_dump_line('Serial')]
items << ['Time', wrap.get_dump_line('Time')]
revoked.extensions.each do |ext|
items << [ext.oid, wrap.get_dump_line(ext.oid)]
end
show_items(revoked, items)
end
def show_request(req)
wrap = RequestDump.new(req)
items = []
items << ['Version', wrap.get_dump_line('Version')]
items << ['Signature Algorithm', wrap.get_dump_line('Signature Algorithm')]
items << ['Subject', wrap.get_dump_line('Subject')]
items << ['Public key', wrap.get_dump_line('Public key')]
req.attributes.each do |attr|
items << [attr.attr, wrap.get_dump_line(attr.oid)]
end
show_items(req, items)
end
def show_items(obj, items)
set_column_size(items.size)
items.each_with_index do |ele, idx|
tag, value = ele
@table.setItemText(idx, 0, tag)
@table.getItem(idx, 0).data = tag
@table.setItemText(idx, 1, value.to_s)
@table.getItem(idx, 1).data = tag
end
@table.connect(SEL_COMMAND) do |sender, sel, loc|
item = @table.getItem(loc.row, loc.col)
@observer.show_detail(obj, item.data)
end
justify_table
end
def set_column_size(size)
col0_width = @table.getColumnWidth(0)
col1_width = @table.getColumnWidth(1)
@table.setTableSize(size, 2)
@table.setColumnWidth(0, col0_width)
@table.setColumnWidth(1, col1_width)
end
def justify_table
for col in 0..@table.numCols-1
for row in 0..@table.numRows-1
@table.getItem(row, col).justify = FXTableItem::LEFT
end
end
end
end
class CertDetail
def initialize(observer, detail)
@observer = observer
@detail = detail
end
def show(item, tag)
if item.nil?
@detail.text = ''
return
end
case item
when OpenSSL::X509::Certificate
show_cert(item, tag)
when OpenSSL::X509::CRL
show_crl(item, tag)
when OpenSSL::X509::Revoked
show_revoked(item, tag)
when OpenSSL::X509::Request
show_request(item, tag)
else
raise NotImplementedError.new("Unknown item type #{item.class}.")
end
end
private
def show_cert(cert, tag)
wrap = CertDump.new(cert)
@detail.text = wrap.get_dump(tag)
end
def show_crl(crl, tag)
wrap = CrlDump.new(crl)
@detail.text = wrap.get_dump(tag)
end
def show_revoked(revoked, tag)
wrap = RevokedDump.new(revoked)
@detail.text = wrap.get_dump(tag)
end
def show_request(request, tag)
wrap = RequestDump.new(request)
@detail.text = wrap.get_dump(tag)
end
end
attr_reader :cert_store
def initialize(app, cert_store)
@cert_store = cert_store
@verify_filter = 0
@verify_filename = nil
full_width = 800
full_height = 500
horz_pos = 300
super(app, "Certificate store", nil, nil, DECOR_ALL, 0, 0, full_width,
full_height)
FXTooltip.new(self.getApp())
menubar = FXMenubar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
file_menu = FXMenuPane.new(self)
FXMenuTitle.new(menubar, "&File", nil, file_menu)
file_open_menu = FXMenuPane.new(self)
FXMenuCommand.new(file_open_menu, "&Directory\tCtl-O").connect(SEL_COMMAND,
method(:on_cmd_file_open_dir))
FXMenuCascade.new(file_menu, "&Open\tCtl-O", nil, file_open_menu)
FXMenuCommand.new(file_menu, "&Quit\tCtl-Q", nil, getApp(), FXApp::ID_QUIT)
tool_menu = FXMenuPane.new(self)
FXMenuTitle.new(menubar, "&Tool", nil, tool_menu)
FXMenuCommand.new(tool_menu, "&Verify\tCtl-N").connect(SEL_COMMAND,
method(:on_cmd_tool_verify))
FXMenuCommand.new(tool_menu, "&Show Request\tCtl-R").connect(SEL_COMMAND,
method(:on_cmd_tool_request))
base_frame = FXHorizontalFrame.new(self, LAYOUT_FILL_X | LAYOUT_FILL_Y)
splitter_horz = FXSplitter.new(base_frame, LAYOUT_SIDE_TOP | LAYOUT_FILL_X |
LAYOUT_FILL_Y | SPLITTER_TRACKING | SPLITTER_HORIZONTAL)
# Cert tree
cert_tree_frame = FXHorizontalFrame.new(splitter_horz, LAYOUT_FILL_X |
LAYOUT_FILL_Y | FRAME_SUNKEN | FRAME_THICK)
cert_tree_frame.setWidth(horz_pos)
cert_tree = FXTreeList.new(cert_tree_frame, 0, nil, 0,
TREELIST_BROWSESELECT | TREELIST_SHOWS_LINES | TREELIST_SHOWS_BOXES |
TREELIST_ROOT_BOXES | LAYOUT_FILL_X | LAYOUT_FILL_Y)
@cert_tree = CertTree.new(self, cert_tree)
# Cert info
splitter_vert = FXSplitter.new(splitter_horz, LAYOUT_SIDE_TOP |
LAYOUT_FILL_X | LAYOUT_FILL_Y | SPLITTER_TRACKING | SPLITTER_VERTICAL |
SPLITTER_REVERSED)
cert_list_base = FXVerticalFrame.new(splitter_vert, LAYOUT_FILL_X |
LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0)
cert_list_frame = FXHorizontalFrame.new(cert_list_base, FRAME_SUNKEN |
FRAME_THICK | LAYOUT_FILL_X | LAYOUT_FILL_Y)
cert_info = FXTable.new(cert_list_frame, 2, 10, nil, 0, FRAME_SUNKEN |
TABLE_COL_SIZABLE | LAYOUT_FILL_X | LAYOUT_FILL_Y, 0, 0, 0, 0, 2, 2, 2, 2)
@cert_info = CertInfo.new(self, cert_info)
cert_detail_base = FXVerticalFrame.new(splitter_vert, LAYOUT_FILL_X |
LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0)
cert_detail_frame = FXHorizontalFrame.new(cert_detail_base, FRAME_SUNKEN |
FRAME_THICK | LAYOUT_FILL_X | LAYOUT_FILL_Y)
cert_detail = FXText.new(cert_detail_frame, nil, 0, TEXT_READONLY |
LAYOUT_FILL_X | LAYOUT_FILL_Y)
@cert_detail = CertDetail.new(self, cert_detail)
show_init
end
def create
super
show(PLACEMENT_SCREEN)
end
def show_init
@cert_tree.show(@cert_store)
show_item(nil)
end
def show_certs
@cert_tree.show_certs(@cert_store)
end
def show_request(req)
@cert_tree.show_request(req)
end
def show_verify_path(verify_path)
@cert_tree.show_verify_path(verify_path)
end
def show_item(item)
@cert_info.show(item) if @cert_info
end
def show_detail(item, tag)
@cert_detail.show(item, tag) if @cert_detail
end
def verify(certfile)
path = verify_certfile(certfile)
show_certs # CRL could be change.
show_verify_path(path)
end
private
def on_cmd_file_open_dir(sender, sel, ptr)
dir = FXFileDialog.getOpenDirectory(self, "Open certificate directory", ".")
unless dir.empty?
begin
@cert_store = CertStore.new(dir)
rescue
show_error($!)
end
show_init
end
1
end
def on_cmd_tool_verify(sender, sel, ptr)
dialog = FXFileDialog.new(self, "Verify certificate")
dialog.filename = ''
dialog.patternList = ["All Files (*)", "PEM formatted certificate (*.pem)"]
dialog.currentPattern = @verify_filter
if dialog.execute != 0
@verify_filename = dialog.filename
verify(@verify_filename)
end
@verify_filter = dialog.currentPattern
1
end
def on_cmd_tool_request(sender, sel, ptr)
dialog = FXFileDialog.new(self, "Show request")
dialog.filename = ''
dialog.patternList = ["All Files (*)", "PEM formatted certificate (*.pem)"]
if dialog.execute != 0
req = @cert_store.generate_cert(dialog.filename)
show_request(req)
end
1
end
def verify_certfile(filename)
begin
cert = @cert_store.generate_cert(filename)
result = @cert_store.verify(cert)
@cert_store.scan_certs
result
rescue
show_error($!)
[]
end
end
def show_error(e)
msg = e.inspect + "\n" + e.backtrace.join("\n")
FXMessageBox.error(self, MBOX_OK, "Error", msg)
end
end
getopts nil, "cert:"
certs_dir = ARGV.shift or raise "#{$0} cert_dir"
certfile = $OPT_cert
app = FXApp.new("CertStore", "FoxTest")
cert_store = CertStore.new(certs_dir)
w = CertStoreView.new(app, cert_store)
app.create
if certfile
w.verify(certfile)
end
app.run

View file

@ -0,0 +1,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

View file

@ -0,0 +1,122 @@
begin
require 'http-access2'
rescue LoadError
STDERR.puts("Cannot load http-access2. CRL might not be fetched.")
end
require 'c_rehash'
class CrlStore
def initialize(c_store)
@c_store = c_store
@c_store.hash_dir(true)
end
def find_crl(cert)
do_find_crl(cert)
end
private
def do_find_crl(cert)
unless ca = find_ca(cert)
return nil
end
unless crlfiles = @c_store.get_crls(ca.subject)
if crl = renew_crl(cert, ca)
@c_store.add_crl(crl)
return crl
end
return nil
end
crlfiles.each do |crlfile|
next unless crl = load_crl(crlfile)
if crl.next_update < Time.now
if new_crl = renew_crl(cert, ca)
@c_store.delete_crl(crl)
@c_store.add_crl(new_crl)
crl = new_crl
end
end
if check_valid(crl, ca)
return crl
end
end
nil
end
def find_ca(cert)
@c_store.get_certs(cert.issuer).each do |cafile|
ca = load_cert(cafile)
if cert.verify(ca.public_key)
return ca
end
end
nil
end
def fetch(location)
if /\AURI:(.*)\z/ =~ location
begin
c = HTTPAccess2::Client.new(ENV['http_proxy'] || ENV['HTTP_PROXY'])
c.get_content($1)
rescue NameError, StandardError
nil
end
else
nil
end
end
def load_cert(certfile)
load_cert_str(File.read(certfile))
end
def load_crl(crlfile)
load_crl_str(File.read(crlfile))
end
def load_cert_str(cert_str)
OpenSSL::X509::Certificate.new(cert_str)
end
def load_crl_str(crl_str)
OpenSSL::X509::CRL.new(crl_str)
end
def check_valid(crl, ca)
unless crl.verify(ca.public_key)
return false
end
crl.last_update <= Time.now
end
RE_CDP = /\AcrlDistributionPoints\z/
def get_cdp(cert)
if cdp_ext = cert.extensions.find { |ext| RE_CDP =~ ext.oid }
cdp_ext.value.chomp
else
false
end
end
def renew_crl(cert, ca)
if cdp = get_cdp(cert)
if new_crl_str = fetch(cdp)
new_crl = load_crl_str(new_crl_str)
if check_valid(new_crl, ca)
return new_crl
end
end
end
false
end
end
if $0 == __FILE__
dir = "trust_certs"
c_store = CHashDir.new(dir)
s = CrlStore.new(c_store)
c = OpenSSL::X509::Certificate.new(File.read("cert_store/google_codesign.pem"))
p s.find_crl(c)
end