Browse Source

Test cert signature for server

new-scoring
aeris 2 years ago
parent
commit
ef0834900f

+ 7
- 6
lib/cryptcheck/tls/cert.rb View File

@@ -5,7 +5,8 @@ module CryptCheck
'/usr/share/ca-certificates/mozilla'
]

SIGNATURE_ALGORITHMS = {
SIGNATURE_ALGORITHMS = %i(md2 mdc2 md4 md5 ripemd160 sha sha1 sha2 rsa dss ecc ghost).freeze
SIGNATURE_ALGORITHMS_X509 = {
'dsaWithSHA' => %i(sha1 dss),
'dsaWithSHA1' => %i(sha1 dss),
'dsaWithSHA1_2' => %i(sha1 dss),
@@ -41,15 +42,15 @@ module CryptCheck
'id_GostR3411_94_with_GostR3410_94' => %i(ghost),
'id_GostR3411_94_with_GostR3410_94_cc' => %i(ghost),
'id_GostR3411_94_with_GostR3410_2001_cc' => %i(ghost)
}
WEAK_SIGN = {
}.freeze
WEAK_SIGN = {
critical: %i(mdc2 md2 md4 md5 sha sha1)
}
}.freeze

%i(md2 mdc2 md4 md5 ripemd160 sha sha1 sha2 rsa dss ecc ghost).each do |name|
SIGNATURE_ALGORITHMS.each do |name|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}?
SIGNATURE_ALGORITHMS[@cert.signature_algorithm].include? :#{name}
SIGNATURE_ALGORITHMS_X509[@cert.signature_algorithm].include? :#{name}
end
RUBY_EVAL
end

+ 1
- 1
lib/cryptcheck/tls/grade.rb View File

@@ -108,7 +108,7 @@ module CryptCheck
#[:aead_only, Proc.new { |s| s.aead_only? }, :best],
] + Cert::WEAK_SIGN.collect do |level, hashes|
hashes.collect do |hash|
["#{hash}_sig?".to_sym, Proc.new { |s| s.certs.any? &"#{hash}?".to_sym }, level ]
["#{hash}_sig?".to_sym, Proc.new { |s| s.call "#{hash}_sig?".to_sym }, level ]
end
end.flatten(1)).freeze


+ 10
- 3
lib/cryptcheck/tls/server.rb View File

@@ -50,7 +50,6 @@ module CryptCheck
check_fallback_scsv

verify_certs
exit
end

def supported_method?(method)
@@ -456,7 +455,7 @@ module CryptCheck
# { curve => connection, ... }
certs += @ecdsa_certs.values
# For anonymous cipher, there is no certificate at all
certs = certs.reject { |c| c.peer_cert.nil? }
certs = certs.reject { |c| c.peer_cert.nil? }
# Then, fetch cert
certs = certs.collect { |c| Cert.new c }
# Then, filter cert to keep uniq fingerprint
@@ -479,7 +478,7 @@ module CryptCheck
Logger.info { ' Trust : ' + 'untrusted'.colorize(:error) + ' - ' + trust }
end
end
@keys = @certs.collect &:key
@keys = @certs.collect &:key
end

def uniq_dh
@@ -493,6 +492,14 @@ module CryptCheck
end
@dh = dh
end

Cert::SIGNATURE_ALGORITHMS.each do |s|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{s}_sign?
@certs.any? &:#{s}?
end
RUBY_EVAL
end
end

class TcpServer < Server

+ 53
- 0
spec/cryptcheck/tls/server_spec.rb View File

@@ -0,0 +1,53 @@
require 'faketime'

describe CryptCheck::Tls::Server do
before :all do
FakeTime.freeze Time.utc(2000, 1, 1).to_i
end

after :all do
FakeTime.unfreeze
end

describe '#md5_sign?' do
it 'must detect server using MD5 certificate' do
tls_serv do
server = CryptCheck::Tls::TcpServer.new 'localhost', ::Socket::PF_INET, '127.0.0.1', 5000
expect(server.md5_sign?).to be false
end

tls_serv material: [:md5, [:rsa, 1024]] do
server = CryptCheck::Tls::TcpServer.new 'localhost', ::Socket::PF_INET, '127.0.0.1', 5000
expect(server.md5_sign?).to be true
end
end
end

describe '#sha1_sign?' do
it 'must detect server using SHA1 certificate' do
tls_serv do
server = CryptCheck::Tls::TcpServer.new 'localhost', ::Socket::PF_INET, '127.0.0.1', 5000
expect(server.sha1_sign?).to be false
end

tls_serv material: [:sha1, [:rsa, 1024]] do
server = CryptCheck::Tls::TcpServer.new 'localhost', ::Socket::PF_INET, '127.0.0.1', 5000
expect(server.sha1_sign?).to be true
end
end
end

describe '#sha2_sign?' do
it 'must detect server using SHA2 certificate' do
tls_serv do
server = CryptCheck::Tls::TcpServer.new 'localhost', ::Socket::PF_INET, '127.0.0.1', 5000
expect(server.sha2_sign?).to be true
end

tls_serv material: [:md5, :sha1] do
server = CryptCheck::Tls::TcpServer.new 'localhost', ::Socket::PF_INET, '127.0.0.1', 5000
expect(server.sha2_sign?).to be false
end
end
end
end

+ 57
- 65
spec/helpers.rb View File

@@ -1,61 +1,42 @@
$:.unshift File.expand_path File.join File.dirname(__FILE__), '../lib'
require 'rubygems'
require 'bundler/setup'
Bundler.require :default, :development
require 'cryptcheck'
Dir['./spec/**/support/**/*.rb'].sort.each { |f| require f }

CryptCheck::Logger.level = ENV['LOG'] || :none

module Helpers
DEFAULT_KEY = 'rsa-1024'
DEFAULT_METHOD = :TLSv1_2
DEFAULT_CIPHERS = %w(AES128-SHA)
DEFAULT_ECC_CURVE = 'secp256k1'
DEFAULT_DH_SIZE = 1024

OpenSSL::PKey::EC.send :alias_method, :private?, :private_key?

def key(name)
open(File.join(File.dirname(__FILE__), 'resources', "#{name}.pem"), 'r') { |f| OpenSSL::PKey.read f }
DEFAULT_METHODS = %i(TLSv1_2)
DEFAULT_CIPHERS = %i(ECDHE+AES)
DEFAULT_CURVES = %i(prime256v1)
DEFAULT_DH = [:rsa, 4096]
DEFAULT_MATERIAL = [[:ecdsa, :prime256v1]]
DEFAULT_CHAIN = %w(intermediate ca)
DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 5000

def key(type, name=nil)
name = if name
"#{type}-#{name}"
else
type
end
OpenSSL::PKey.read File.read "spec/resources/#{name}.pem"
end

def dh(name)
open(File.join(File.dirname(__FILE__), 'resources', "dh-#{name}.pem"), 'r') { |f| OpenSSL::PKey::DH.new f }
def cert(type, name=nil)
name = if name
"#{type}-#{name}"
else
type
end
OpenSSL::X509::Certificate.new File.read "spec/resources/#{name}.crt"
end

def certificate(key, domain)
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = rand 2**(20*8-1) .. 2**(20*8)
cert.not_before = Time.now
cert.not_after = cert.not_before + 60*60

cert.public_key = case key
when OpenSSL::PKey::EC
curve = key.group.curve_name
public = OpenSSL::PKey::EC.new curve
public.public_key = key.public_key
public
else
key.public_key
end

name = OpenSSL::X509::Name.parse "CN=#{domain}"
cert.subject = name
cert.issuer = name

extension_factory = OpenSSL::X509::ExtensionFactory.new nil, cert
extension_factory.subject_certificate = cert
extension_factory.issuer_certificate = cert

cert.add_extension extension_factory.create_extension 'basicConstraints', 'CA:TRUE', true
cert.add_extension extension_factory.create_extension 'keyUsage', 'keyEncipherment, dataEncipherment, digitalSignature,nonRepudiation,keyCertSign'
cert.add_extension extension_factory.create_extension 'extendedKeyUsage', 'serverAuth, clientAuth'
cert.add_extension extension_factory.create_extension 'subjectKeyIdentifier', 'hash'
cert.add_extension extension_factory.create_extension 'authorityKeyIdentifier', 'keyid:always'
cert.add_extension extension_factory.create_extension 'subjectAltName', "DNS:#{domain}"

cert.sign key, OpenSSL::Digest::SHA512.new
def dh(name)
OpenSSL::PKey::DH.new File.read "spec/resources/dh-#{name}.pem"
end

def serv(server, process, &block)
@@ -95,32 +76,43 @@ module Helpers
end
end

def context(key: DEFAULT_KEY, domain: 'localhost', # Key & certificate
version: DEFAULT_METHOD, ciphers: DEFAULT_CIPHERS, # TLS version and ciphers
dh: DEFAULT_DH_SIZE, ecdh: DEFAULT_ECC_CURVE) # DHE & ECDHE
key = key key
cert = certificate key, domain
def context(certs, keys, chain=[],
methods: DEFAULT_METHODS, ciphers: DEFAULT_CIPHERS,
dh:, curves: DEFAULT_CURVES, server_preference: true)
context = OpenSSL::SSL::SSLContext.new

context = OpenSSL::SSL::SSLContext.new version
context.cert = cert
context.key = key
context.ciphers = ciphers
context.options |= OpenSSL::SSL::OP_NO_SSLv2 unless methods.include? :SSLv2
context.options |= OpenSSL::SSL::OP_NO_SSLv3 unless methods.include? :SSLv3
context.options |= OpenSSL::SSL::OP_NO_TLSv1 unless methods.include? :TLSv1
context.options |= OpenSSL::SSL::OP_NO_TLSv1_1 unless methods.include? :TLSv1_1
context.options |= OpenSSL::SSL::OP_NO_TLSv1_2 unless methods.include? :TLSv1_2
context.options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if server_preference

if dh
dh = dh dh
context.tmp_dh_callback = proc { dh }
end
context.ecdh_curves = ecdh if ecdh
context.certs = certs
context.keys = keys
context.extra_chain_cert = chain if chain

context.ciphers = ciphers.join ':'
context.tmp_dh_callback = proc { dh } if dh
context.ecdh_curves = curves.join ':' if curves

context
end

def tls_serv(key: DEFAULT_KEY, domain: 'localhost', # Key & certificate
version: DEFAULT_METHOD, ciphers: DEFAULT_CIPHERS, # TLS version and ciphers
dh: DEFAULT_DH_SIZE, ecdh: DEFAULT_ECC_CURVE, # DHE & ECDHE
host: '127.0.0.1', port: 5000, # Binding
def tls_serv(host: DEFAULT_HOST, port: DEFAULT_PORT,
material: DEFAULT_MATERIAL, chain: DEFAULT_CHAIN,
methods: DEFAULT_METHODS, ciphers: DEFAULT_CIPHERS,
dh: nil, curves: DEFAULT_CURVES, server_preference: true,
process: nil, &block)
context = context(key: key, domain: domain, version: version, ciphers: ciphers, dh: dh, ecdh: ecdh)
keys = material.collect { |m| key *m }
certs = material.collect { |m| cert *m }
chain = chain.collect { |c| cert c }
dh = dh dh if dh

context = context certs, keys, chain,
methods: methods, ciphers: ciphers,
dh: dh, curves: curves,
server_preference: server_preference
tcp_server = TCPServer.new host, port
tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context
begin
@@ -131,7 +123,7 @@ module Helpers
end
end

def plain_serv(host: '127.0.0.1', port: 5000, process: nil, &block)
def plain_serv(host='127.0.0.1', port=5000, process: nil, &block)
tcp_server = TCPServer.new host, port
begin
serv tcp_server, process, &block

Loading…
Cancel
Save