From 5c08e8c44b608ed9e9fb711422e1b5656679fcd4 Mon Sep 17 00:00:00 2001 From: aeris Date: Mon, 23 Jan 2017 22:34:12 +0100 Subject: [PATCH] Encapsulate certificate/chain --- lib/cryptcheck/tls/cert.rb | 45 ++++++++++++++++++++++++++++++++---- lib/cryptcheck/tls/server.rb | 35 +++++++++++----------------- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/lib/cryptcheck/tls/cert.rb b/lib/cryptcheck/tls/cert.rb index 967c437..d34b214 100644 --- a/lib/cryptcheck/tls/cert.rb +++ b/lib/cryptcheck/tls/cert.rb @@ -45,17 +45,24 @@ module CryptCheck %i(md2 mdc2 md4 md5 ripemd160 sha sha1 sha2 rsa dss ecc ghost).each do |name| class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{name}_sig? - @chains.any? do |chain| - SIGNATURE_ALGORITHMS[chain[:cert].signature_algorithm].include? :#{name} - end + def #{name}? + SIGNATURE_ALGORITHMS[@cert.signature_algorithm].include? :#{name} end RUBY_EVAL end + def initialize(cert, chain=[]) + @cert, @chain = case cert + when ::OpenSSL::X509::Certificate + [cert, chain] + when ::OpenSSL::SSL::SSLSocket + [cert.peer_cert, cert.peer_cert_chain] + end + end + def self.trusted?(cert, chain, roots: DEFAULT_CA_DIRECTORIES) store = ::OpenSSL::X509::Store.new - store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + store.purpose = ::OpenSSL::X509::PURPOSE_SSL_CLIENT store.add_chains roots chain.each do |cert| # Never add other self signed certificates than system CA ! @@ -67,6 +74,34 @@ module CryptCheck return :trusted if trusted store.error_string end + + def trusted?(roots: DEFAULT_CA_DIRECTORIES) + Cert.trusted? @cert, @chain, roots: roots + end + + def valid?(host) + ::OpenSSL::SSL.verify_certificate_identity @cert, host + end + + def fingerprint + ::OpenSSL::Digest::SHA256.hexdigest @cert.to_der + end + + def key + @cert.public_key + end + + def subject + @cert.subject + end + + def serial + @cert.serial + end + + def issuer + @cert.issuer + end end end end diff --git a/lib/cryptcheck/tls/server.rb b/lib/cryptcheck/tls/server.rb index 6dcc373..ec71ecb 100644 --- a/lib/cryptcheck/tls/server.rb +++ b/lib/cryptcheck/tls/server.rb @@ -447,29 +447,21 @@ module CryptCheck # Let's begin the fun # First, collect "standard" connections # { method => { cipher => connection, ... }, ... } - certs = @supported_ciphers.values.collect(&:values).flatten 1 + certs = @supported_ciphers.values.collect(&:values).flatten 1 # Then, collect "ecdsa" connections # { curve => connection, ... } - certs += @ecdsa_certs.values - # Then, fetch cert and chain - certs = certs.collect { |c| [c.peer_cert, c.peer_cert_chain] } - # Then, filter cert to keep uniq subject + issuer + serial - #certs = certs.uniq { |c, _| [c.subject, c.serial, c.issuer] } + certs += @ecdsa_certs.values + # Then, fetch cert + certs = certs.collect { |c| Cert.new c } # Then, filter cert to keep uniq fingerprint - certs = certs.uniq { |c, _| OpenSSL::Digest::SHA256.hexdigest c.to_der } - - view = {} - certs.each do |cert, chain| - id = cert.subject, cert.serial, cert.issuer - next if view.include? id - subject, serial, issuer = id - key = cert.public_key - - identity = ::OpenSSL::SSL.verify_certificate_identity cert, (@hostname || @ip) - trust = Cert.trusted? cert, chain - view[id] = { cert: cert, chain: chain, key: key, identity: identity, trust: trust } - Logger.info { " Certificate #{subject} [#{serial}] issued by #{issuer}" } - Logger.info { ' Key : ' + Tls.key_to_s(key) } + @certs = certs.uniq { |c| c.fingerprint } + + @certs.each do |cert| + key = cert.key + identity = cert.valid?(@hostname || @ip) + trust = cert.trusted? + Logger.info { " Certificate #{cert.subject} [#{cert.serial}] issued by #{cert.issuer}" } + Logger.info { ' Key : ' + Tls.key_to_s(key) } if identity Logger.info { ' Identity : ' + 'valid'.colorize(:good) } else @@ -481,8 +473,7 @@ module CryptCheck Logger.info { ' Trust : ' + 'untrusted'.colorize(:error) + ' - ' + trust } end end - @chains = view.values - @keys = @chains.collect { |c| c[:key] } + @keys = @certs.collect &:key end def uniq_dh