Module: SSLTest

Extended by:
CRL, OCSP
Defined in:
lib/ssl-test.rb,
lib/ssl-test/crl.rb,
lib/ssl-test/ocsp.rb,
lib/ssl-test/memory_store.rb

Defined Under Namespace

Modules: CRL, OCSP Classes: MemoryStore

Constant Summary collapse

VERSION =
-"2.0.0"
CACHE_NAMESPACE =

Prefix for all cache keys so SSLTest entries coexist cleanly inside a shared cache (e.g. Rails.cache).

-"ssl-test"

Constants included from OCSP

OCSP::ERROR_CACHE_DURATION

Constants included from CRL

CRL::CRL_CACHE_DURATION, CRL::CRL_CACHE_RETENTION

Class Method Summary collapse

Class Method Details

.cacheObject

The cache backend used to store CRL and OCSP responses. Defaults to an in-process MemoryStore. To share the cache across processes (and get compression), assign Rails.cache (or any object responding to the Rails.cache-style API: read/write/delete), e.g. ‘SSLTest.cache = Rails.cache`.



91
92
93
# File 'lib/ssl-test.rb', line 91

def cache
  @cache ||= MemoryStore.new
end

.cache=(store) ⇒ Object



95
96
97
# File 'lib/ssl-test.rb', line 95

def cache= store
  @cache = store
end

.cache_sizeObject

Removed in 2.0: introspection now lives on the cache store. With the built-in MemoryStore use SSLTest.cache.size; other backends (e.g. memcache) can’t be enumerated.

Raises:

  • (NoMethodError)


102
103
104
# File 'lib/ssl-test.rb', line 102

def cache_size
  raise NoMethodError, "SSLTest.cache_size was removed in 2.0; use SSLTest.cache.size instead (available on the built-in SSLTest::MemoryStore)."
end

.flush_cacheObject

Removed in 2.0: clearing now lives on the cache store. With the built-in MemoryStore use SSLTest.cache.clear (note: calling clear on a shared backend like Rails.cache would wipe unrelated entries).

Raises:

  • (NoMethodError)


109
110
111
# File 'lib/ssl-test.rb', line 109

def flush_cache
  raise NoMethodError, "SSLTest.flush_cache was removed in 2.0; use SSLTest.cache.clear instead."
end

.logger=(logger) ⇒ Object



113
114
115
# File 'lib/ssl-test.rb', line 113

def logger= logger
  @logger = logger
end

.test_cert(client_cert, ca_certs, open_timeout: 5, read_timeout: 5, proxy_host: nil, proxy_port: nil, redirection_limit: 5) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/ssl-test.rb', line 58

def test_cert client_cert, ca_certs, open_timeout: 5, read_timeout: 5, proxy_host:nil, proxy_port: nil, redirection_limit: 5
  cert = failed_cert_reason = chain = store = nil

  store = OpenSSL::X509::Store.new
  ca_certs.each { store.add_cert(_1) }
  store.verify_callback = -> (verify_ok, store_context) {
    cert = store_context.current_cert
    chain = store_context.chain
    failed_cert_reason = [store_context.error, store_context.error_string] if store_context.error != 0
    verify_ok
  }

  begin
    store.verify(client_cert)

    if failed_cert_reason
      error_message = "error code #{failed_cert_reason[0]}: #{failed_cert_reason[1]}"
      @logger&.info { "SSLTest #{cert.subject.to_s} finished: #{error_message}" }
      return [false, error_message, cert]
    else
      revoked, message, revocation_date = test_chain_revocation(chain, open_timeout: open_timeout, read_timeout: read_timeout, proxy_host: proxy_host, proxy_port: proxy_port, redirection_limit: redirection_limit)
      return [!revoked, revocation_message(revoked, revocation_date, message), cert]
    end
  rescue => error
    @logger&.error { "SSLTest #{cert.subject.to_s} failed: #{error.message}" }
    return [nil, "SSL certificate test failed: #{error.message}", cert]
  end
end

.test_url(url, open_timeout: 5, read_timeout: 5, proxy_host: nil, proxy_port: nil, redirection_limit: 5) ⇒ Object Also known as: test



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/ssl-test.rb', line 21

def test_url url, open_timeout: 5, read_timeout: 5, proxy_host: nil, proxy_port: nil, redirection_limit: 5
  cert = failed_cert_reason = chain = nil

  uri = URI.parse(url)
  return if uri.scheme != 'https' and uri.scheme != 'tcps'

  @logger&.info { "SSLTest #{url} started" }
  http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port)
  http.open_timeout = open_timeout
  http.read_timeout = read_timeout
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
  http.verify_callback = -> (verify_ok, store_context) {
    cert = store_context.current_cert
    chain = store_context.chain
    failed_cert_reason = [store_context.error, store_context.error_string] if store_context.error != 0
    verify_ok
  }

  begin
    http.start { }

    revoked, message, revocation_date = test_chain_revocation(chain, open_timeout: open_timeout, read_timeout: read_timeout, proxy_host: proxy_host, proxy_port: proxy_port, redirection_limit: redirection_limit)
    @logger&.info { "SSLTest #{url} finished: revoked=#{revoked} #{message}" }
    return [!revoked, revocation_message(revoked, revocation_date, message), cert]
  rescue OpenSSL::SSL::SSLError => error
    error_message = parse_ssl_error(error, cert, failed_cert_reason, uri:)
    @logger&.info { "SSLTest #{url} finished: #{error_message}" }
    return [false, error_message, cert]
  rescue => error
    @logger&.error { "SSLTest #{url} failed: #{error.message}" }
    return [nil, "SSL certificate test failed: #{error.message}", cert]
  end
end