|
|
|
@ -0,0 +1,123 @@ |
|
|
|
|
#!/usr/bin/env ruby |
|
|
|
|
$:.unshift File.expand_path File.join File.dirname(__FILE__), '../lib' |
|
|
|
|
require 'rubygems' |
|
|
|
|
require 'bundler/setup' |
|
|
|
|
require 'openssl' |
|
|
|
|
require 'socket' |
|
|
|
|
require 'cryptcheck' |
|
|
|
|
|
|
|
|
|
::CryptCheck::Logger.level = ENV['LOG'] || :info |
|
|
|
|
|
|
|
|
|
OpenSSL::PKey::EC.send :alias_method, :private?, :private_key? |
|
|
|
|
|
|
|
|
|
# [512, 768, 1024, 2048, 3072, 4096].each do |s| |
|
|
|
|
# file = "config/rsa-#{s}.pem" |
|
|
|
|
# unless File.exists? file |
|
|
|
|
# puts :rsa, s |
|
|
|
|
# dh = OpenSSL::PKey::RSA.new s |
|
|
|
|
# File.write file, dh.to_pem |
|
|
|
|
# end |
|
|
|
|
# |
|
|
|
|
# file = "config/dh-#{s}.pem" |
|
|
|
|
# unless File.exists? file |
|
|
|
|
# puts :dh, s |
|
|
|
|
# dh = OpenSSL::PKey::DH.new s |
|
|
|
|
# File.write file, dh.to_pem |
|
|
|
|
# end |
|
|
|
|
# end |
|
|
|
|
# exit |
|
|
|
|
|
|
|
|
|
def certificate(key) |
|
|
|
|
CryptCheck::Logger.info 'Generating certificate' |
|
|
|
|
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 = Time.now + 365*24*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=localhost' |
|
|
|
|
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:localhost' |
|
|
|
|
|
|
|
|
|
cert.add_extension OpenSSL::X509::Extension.new '1.3.6.1.5.5.7.1.24', '0', true |
|
|
|
|
|
|
|
|
|
cert.sign key, OpenSSL::Digest::SHA512.new |
|
|
|
|
CryptCheck::Logger.info 'Certificate generated' |
|
|
|
|
cert |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
key = OpenSSL::PKey::RSA.new File.read 'config/rsa-2048.pem' |
|
|
|
|
#key = OpenSSL::PKey::EC.new('secp521r1').generate_key |
|
|
|
|
cert = certificate key |
|
|
|
|
|
|
|
|
|
CryptCheck::Logger.info 'Starting server' |
|
|
|
|
|
|
|
|
|
context = OpenSSL::SSL::SSLContext.new |
|
|
|
|
#context = OpenSSL::SSL::SSLContext.new :SSLv3 |
|
|
|
|
#context = OpenSSL::SSL::SSLContext.new :TLSv1_1 |
|
|
|
|
context.cert = cert |
|
|
|
|
context.key = key |
|
|
|
|
context.ciphers = ARGV[0] || 'ECDHE+AESGCM' |
|
|
|
|
|
|
|
|
|
dh = OpenSSL::PKey::DH.new File.read 'config/dh-4096.pem' |
|
|
|
|
context.tmp_dh_callback = proc { dh } |
|
|
|
|
#context.ecdh_curves = CryptCheck::Tls::Server::SUPPORTED_CURVES.join ':' |
|
|
|
|
#context.ecdh_curves = 'secp384r1:secp521r1:sect571r1' |
|
|
|
|
context.ecdh_curves = 'secp384r1' |
|
|
|
|
#ecdh = OpenSSL::PKey::EC.new('secp384r1').generate_key |
|
|
|
|
#context.tmp_ecdh_callback = proc { ecdh } |
|
|
|
|
|
|
|
|
|
host, port = '::', 5000 |
|
|
|
|
tcp_server = TCPServer.new host, port |
|
|
|
|
tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context |
|
|
|
|
::CryptCheck::Logger.info "Server started on #{host}:#{port}" |
|
|
|
|
|
|
|
|
|
loop do |
|
|
|
|
begin |
|
|
|
|
connection = tls_server.accept |
|
|
|
|
|
|
|
|
|
method = connection.ssl_version |
|
|
|
|
|
|
|
|
|
dh = connection.tmp_key |
|
|
|
|
cipher = connection.cipher |
|
|
|
|
cipher = CryptCheck::Tls::Cipher.new method, cipher, dh |
|
|
|
|
states = cipher.states |
|
|
|
|
text = %i(critical error warning good perfect best).collect do |s| |
|
|
|
|
states[s].collect { |t| t.to_s.colorize s }.join ' ' |
|
|
|
|
end.reject &:empty? |
|
|
|
|
text = text.join ' ' |
|
|
|
|
|
|
|
|
|
dh = dh ? " (#{'PFS'.colorize :good} : #{CryptCheck::Tls.key_to_s dh})" : '' |
|
|
|
|
CryptCheck::Logger.info { "#{CryptCheck::Tls.colorize method} / #{cipher.colorize}#{dh} [#{text}]" } |
|
|
|
|
|
|
|
|
|
data = connection.gets |
|
|
|
|
if data |
|
|
|
|
CryptCheck::Logger.info data |
|
|
|
|
end |
|
|
|
|
connection.puts 'HTTP/1.1 200 OK' |
|
|
|
|
connection.puts 'Strict-Transport-Security: max-age=31536000' |
|
|
|
|
connection.close |
|
|
|
|
rescue OpenSSL::SSL::SSLError, SystemCallError |
|
|
|
|
end |
|
|
|
|
end |