# frozen_string_literal: true # Released under the MIT License. # Copyright, 2018-2021, by Samuel Williams. require 'openssl' module Async module RSpec module SSL module CertificateAuthority end module ValidCertificate end module InvalidCertificate end module VerifiedContexts end module HostCertificates end end ::RSpec.shared_context SSL::CertificateAuthority do # This key size is generally considered insecure, but it's fine for testing. let(:certificate_authority_key) {OpenSSL::PKey::RSA.new(2048)} let(:certificate_authority_name) {OpenSSL::X509::Name.parse("O=TestCA/CN=localhost")} # The certificate authority is used for signing and validating the certificate which is used for communciation: let(:certificate_authority) do certificate = OpenSSL::X509::Certificate.new certificate.subject = certificate_authority_name # We use the same issuer as the subject, which makes this certificate self-signed: certificate.issuer = certificate_authority_name certificate.public_key = certificate_authority_key.public_key certificate.serial = 1 certificate.version = 2 certificate.not_before = Time.now certificate.not_after = Time.now + 3600 extension_factory = OpenSSL::X509::ExtensionFactory.new extension_factory.subject_certificate = certificate extension_factory.issuer_certificate = certificate certificate.add_extension extension_factory.create_extension("basicConstraints", "CA:TRUE", true) certificate.add_extension extension_factory.create_extension("keyUsage", "keyCertSign, cRLSign", true) certificate.add_extension extension_factory.create_extension("subjectKeyIdentifier", "hash") certificate.add_extension extension_factory.create_extension("authorityKeyIdentifier", "keyid:always", false) certificate.sign certificate_authority_key, OpenSSL::Digest::SHA256.new end let(:certificate_store) do # The certificate store which is used for validating the server certificate: OpenSSL::X509::Store.new.tap do |certificates| certificates.add_cert(certificate_authority) end end end ::RSpec.shared_context SSL::ValidCertificate do include_context SSL::CertificateAuthority # The private key to use on the server side: let(:key) {OpenSSL::PKey::RSA.new(2048)} let(:certificate_name) {OpenSSL::X509::Name.parse("O=Test/CN=localhost")} # The certificate used for actual communication: let(:certificate) do certificate = OpenSSL::X509::Certificate.new certificate.subject = certificate_name certificate.issuer = certificate_authority.subject certificate.public_key = key.public_key certificate.serial = 2 certificate.version = 2 certificate.not_before = Time.now certificate.not_after = Time.now + 3600 extension_factory = OpenSSL::X509::ExtensionFactory.new() extension_factory.subject_certificate = certificate extension_factory.issuer_certificate = certificate_authority certificate.add_extension extension_factory.create_extension("keyUsage", "digitalSignature", true) certificate.add_extension extension_factory.create_extension("subjectKeyIdentifier", "hash") certificate.sign certificate_authority_key, OpenSSL::Digest::SHA256.new end end ::RSpec.shared_context SSL::HostCertificates do include_context SSL::CertificateAuthority let(:keys) do Hash[ hosts.collect{|name| [name, OpenSSL::PKey::RSA.new(2048)]} ] end # The certificate used for actual communication: let(:certificates) do Hash[ hosts.collect do |name| certificate_name = OpenSSL::X509::Name.parse("O=Test/CN=#{name}") certificate = OpenSSL::X509::Certificate.new certificate.subject = certificate_name certificate.issuer = certificate_authority.subject certificate.public_key = keys[name].public_key certificate.serial = 2 certificate.version = 2 certificate.not_before = Time.now certificate.not_after = Time.now + 3600 extension_factory = OpenSSL::X509::ExtensionFactory.new extension_factory.subject_certificate = certificate extension_factory.issuer_certificate = certificate_authority certificate.add_extension extension_factory.create_extension("keyUsage", "digitalSignature", true) certificate.add_extension extension_factory.create_extension("subjectKeyIdentifier", "hash") certificate.sign certificate_authority_key, OpenSSL::Digest::SHA256.new [name, certificate] end ] end let(:server_context) do OpenSSL::SSL::SSLContext.new.tap do |context| context.servername_cb = Proc.new do |socket, name| if hosts.include? name socket.hostname = name OpenSSL::SSL::SSLContext.new.tap do |context| context.cert = certificates[name] context.key = keys[name] end end end end end let(:client_context) do OpenSSL::SSL::SSLContext.new.tap do |context| context.cert_store = certificate_store context.verify_mode = OpenSSL::SSL::VERIFY_PEER end end end ::RSpec.shared_context SSL::InvalidCertificate do include_context SSL::CertificateAuthority # The private key to use on the server side: let(:key) {OpenSSL::PKey::RSA.new(2048)} let(:invalid_key) {OpenSSL::PKey::RSA.new(2048)} let(:certificate_name) {OpenSSL::X509::Name.parse("O=Test/CN=localhost")} # The certificate used for actual communication: let(:certificate) do certificate = OpenSSL::X509::Certificate.new certificate.subject = certificate_name certificate.issuer = certificate_authority.subject certificate.public_key = key.public_key certificate.serial = 2 certificate.version = 2 certificate.not_before = Time.now - 3600 certificate.not_after = Time.now extension_factory = OpenSSL::X509::ExtensionFactory.new() extension_factory.subject_certificate = certificate extension_factory.issuer_certificate = certificate_authority certificate.add_extension extension_factory.create_extension("keyUsage", "digitalSignature", true) certificate.add_extension extension_factory.create_extension("subjectKeyIdentifier", "hash") certificate.sign invalid_key, OpenSSL::Digest::SHA256.new end end ::RSpec.shared_context SSL::VerifiedContexts do let(:server_context) do OpenSSL::SSL::SSLContext.new.tap do |context| context.cert = certificate context.key = key end end let(:client_context) do OpenSSL::SSL::SSLContext.new.tap do |context| context.cert_store = certificate_store context.verify_mode = OpenSSL::SSL::VERIFY_PEER end end end end end