ruby/sample/openssl/c_rehash.rb

174 строки
3.7 KiB
Ruby

#!/usr/bin/env ruby
require 'openssl'
class CHashDir
include Enumerable
def initialize(dirpath)
@dirpath = dirpath
@fingerprint_cache = @cert_cache = @crl_cache = nil
end
def hash_dir(silent = false)
# ToDo: Should lock the directory...
@silent = silent
@fingerprint_cache = Hash.new
@cert_cache = Hash.new
@crl_cache = Hash.new
do_hash_dir
end
def get_certs(name = nil)
if name
@cert_cache[hash_name(name)]
else
@cert_cache.values.flatten
end
end
def get_crls(name = nil)
if name
@crl_cache[hash_name(name)]
else
@crl_cache.values.flatten
end
end
def delete_crl(crl)
File.unlink(crl_filename(crl))
hash_dir(true)
end
def add_crl(crl)
File.open(crl_filename(crl), "w") do |f|
f << crl.to_pem
end
hash_dir(true)
end
def load_pem_file(filepath)
str = File.read(filepath)
begin
OpenSSL::X509::Certificate.new(str)
rescue
begin
OpenSSL::X509::CRL.new(str)
rescue
begin
OpenSSL::X509::Request.new(str)
rescue
nil
end
end
end
end
private
def crl_filename(crl)
path(hash_name(crl.issuer)) + '.pem'
end
def do_hash_dir
Dir.chdir(@dirpath) do
delete_symlink
Dir.glob('*.pem') do |pemfile|
cert = load_pem_file(pemfile)
case cert
when OpenSSL::X509::Certificate
link_hash_cert(pemfile, cert)
when OpenSSL::X509::CRL
link_hash_crl(pemfile, cert)
else
STDERR.puts("WARNING: #{pemfile} does not contain a certificate or CRL: skipping") unless @silent
end
end
end
end
def delete_symlink
Dir.entries(".").each do |entry|
next unless /^[\da-f]+\.r{0,1}\d+$/ =~ entry
File.unlink(entry) if FileTest.symlink?(entry)
end
end
def link_hash_cert(org_filename, cert)
name_hash = hash_name(cert.subject)
fingerprint = fingerprint(cert.to_der)
filepath = link_hash(org_filename, name_hash, fingerprint) { |idx|
"#{name_hash}.#{idx}"
}
unless filepath
unless @silent
STDERR.puts("WARNING: Skipping duplicate certificate #{org_filename}")
end
else
(@cert_cache[name_hash] ||= []) << path(filepath)
end
end
def link_hash_crl(org_filename, crl)
name_hash = hash_name(crl.issuer)
fingerprint = fingerprint(crl.to_der)
filepath = link_hash(org_filename, name_hash, fingerprint) { |idx|
"#{name_hash}.r#{idx}"
}
unless filepath
unless @silent
STDERR.puts("WARNING: Skipping duplicate CRL #{org_filename}")
end
else
(@crl_cache[name_hash] ||= []) << path(filepath)
end
end
def link_hash(org_filename, name, fingerprint)
idx = 0
filepath = nil
while true
filepath = yield(idx)
break unless FileTest.symlink?(filepath) or FileTest.exist?(filepath)
if @fingerprint_cache[filepath] == fingerprint
return false
end
idx += 1
end
STDOUT.puts("#{org_filename} => #{filepath}") unless @silent
symlink(org_filename, filepath)
@fingerprint_cache[filepath] = fingerprint
filepath
end
def symlink(from, to)
begin
File.symlink(from, to)
rescue
File.open(to, "w") do |f|
f << File.read(from)
end
end
end
def path(filename)
File.join(@dirpath, filename)
end
def hash_name(name)
sprintf("%x", name.hash)
end
def fingerprint(der)
OpenSSL::Digest.hexdigest('MD5', der).upcase
end
end
if $0 == __FILE__
dirlist = ARGV
dirlist << '/usr/ssl/certs' if dirlist.empty?
dirlist.each do |dir|
CHashDir.new(dir).hash_dir
end
end