class HTTPClient::SSLConfig
then add_trust_ca for that purpose.
You may want to change trust anchor by yourself. Call clear_cert_store
filename extension (p7s), HTTPClient doesn’t verify the signature in it.
‘cacert.p7s’ is automatically generated from JDK 1.6. Regardless its
included in released package.
like Web browsers. ‘httpclient/cacert.p7s’ is created by the author and
This means that HTTPClient instance trusts some CA certificates by default,
(trusted certificate(s)) with add_trust_ca in initialization time.
SSLConfig loads ‘httpclient/cacert.p7s’ as a trust anchor
== Trust Anchor Control
The implementation depends on OpenSSL.
Represents SSL configuration for HTTPClient instance.
def add_crl(crl)
OpenSSL::X509::CRL.
crl:: a OpenSSL::X509::CRL or a filename of a PEM/DER formatted
Adds CRL for verification.
def add_crl(crl) unless crl.is_a?(X509::CRL) crl = X509::CRL.new(File.open(crl) { |f| f.read }) end @cert_store.add_crl(crl) @cert_store.flags = X509::V_FLAG_CRL_CHECK | X509::V_FLAG_CRL_CHECK_ALL change_notify end
def add_trust_ca(trust_ca_file_or_hashed_dir)
trusted certificate files.
a 'c-rehash'eddirectory name which stores
OpenSSL::X509::Certificate or
trust_ca_file_or_hashed_dir:: a filename of a PEM/DER formatted
Sets trust anchor certificate(s) for verification.
def add_trust_ca(trust_ca_file_or_hashed_dir) @cacerts_loaded = true # avoid lazy override add_trust_ca_to_store(@cert_store, trust_ca_file_or_hashed_dir) change_notify end
def add_trust_ca_to_store(cert_store, trust_ca_file_or_hashed_dir)
def add_trust_ca_to_store(cert_store, trust_ca_file_or_hashed_dir) if FileTest.directory?(trust_ca_file_or_hashed_dir) cert_store.add_path(trust_ca_file_or_hashed_dir) else cert_store.add_file(trust_ca_file_or_hashed_dir) end end
def cert_store=(cert_store)
don't use if you don't know what it is.
Sets new certificate store (OpenSSL::X509::Store).
def cert_store=(cert_store) @cacerts_loaded = true # avoid lazy override @cert_store = cert_store change_notify end
def change_notify
def change_notify @client.reset_all nil end
def ciphers=(ciphers)
Sets cipher configuration. New value must be a String.
def ciphers=(ciphers) @ciphers = ciphers change_notify end
def clear_cert_store
new one for the next session.
Drops current certificate store (OpenSSL::X509::Store) for SSL and create
def clear_cert_store @cacerts_loaded = true # avoid lazy override @cert_store = X509::Store.new change_notify end
def client_ca=(client_ca) # :nodoc:
def client_ca=(client_ca) # :nodoc: @client_ca = client_ca change_notify end
def client_cert=(client_cert)
client_key and client_cert must be a pair.
authentication.
Sets certificate (OpenSSL::X509::Certificate) for SSL client
def client_cert=(client_cert) @client_cert = client_cert change_notify end
def client_key=(client_key)
client_key and client_cert must be a pair.
Sets private key (OpenSSL::PKey::PKey) for SSL client authentication.
def client_key=(client_key) @client_key = client_key change_notify end
def default_verify_callback(is_ok, ctx)
def default_verify_callback(is_ok, ctx) if $DEBUG if is_ok warn("ok: #{ctx.current_cert.subject.to_s.dump}") else warn("ng: #{ctx.current_cert.subject.to_s.dump} at depth #{ctx.error_depth} - #{ctx.error}: #{ctx.error_string} in #{ctx.chain.inspect}") end warn(ctx.current_cert.to_text) warn(ctx.current_cert.to_pem) end if !is_ok depth = ctx.error_depth code = ctx.error msg = ctx.error_string warn("at depth #{depth} - #{code}: #{msg}") if $DEBUG end is_ok end
def initialize(client)
def initialize(client) return unless SSLEnabled @client = client @cert_store = X509::Store.new @client_cert = @client_key = @client_ca = nil @verify_mode = SSL::VERIFY_PEER | SSL::VERIFY_FAIL_IF_NO_PEER_CERT @verify_depth = nil @verify_callback = nil @dest = nil @timeout = nil @ssl_version = :auto # Follow ruby-ossl's definition @options = OpenSSL::SSL::OP_ALL @options &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) @options |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) @options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) @options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) # OpenSSL 0.9.8 default: "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH" @ciphers = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default @cacerts_loaded = false end
def load_cacerts(cert_store)
def load_cacerts(cert_store) file = File.join(File.dirname(__FILE__), 'cacert.p7s') add_trust_ca_to_store(cert_store, file) end
def load_trust_ca
Loads default trust anchors.
def load_trust_ca load_cacerts(@cert_store) change_notify end
def options=(options)
OpenSSL::SSL::OP_*
Sets SSL options. New value must be a combination of # constants
def options=(options) @options = options change_notify end
def post_connection_check(peer_cert, hostname) # :nodoc:
this definition must match with the one in ext/openssl/lib/openssl/ssl.rb
post connection check proc for ruby < 1.8.5.
def post_connection_check(peer_cert, hostname) # :nodoc: check_common_name = true cert = peer_cert cert.extensions.each{|ext| next if ext.oid != "subjectAltName" ext.value.split(/,\s+/).each{|general_name| if /\ADNS:(.*)/ =~ general_name check_common_name = false reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+") return true if /\A#{reg}\z/i =~ hostname elsif /\AIP Address:(.*)/ =~ general_name check_common_name = false return true if $1 == hostname end } } if check_common_name cert.subject.to_a.each{|oid, value| if oid == "CN" reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+") return true if /\A#{reg}\z/i =~ hostname end } end raise SSL::SSLError, "hostname was not match with the server certificate" end
def sample_verify_callback(is_ok, ctx)
def sample_verify_callback(is_ok, ctx) unless is_ok depth = ctx.error_depth code = ctx.error msg = ctx.error_string warn("at depth #{depth} - #{code}: #{msg}") if $DEBUG return false end cert = ctx.current_cert self_signed = false ca = false pathlen = nil server_auth = true self_signed = (cert.subject.cmp(cert.issuer) == 0) # Check extensions whatever its criticality is. (sample) cert.extensions.each do |ex| case ex.oid when 'basicConstraints' /CA:(TRUE|FALSE), pathlen:(\d+)/ =~ ex.value ca = ($1 == 'TRUE') pathlen = $2.to_i when 'keyUsage' usage = ex.value.split(/\s*,\s*/) ca = usage.include?('Certificate Sign') server_auth = usage.include?('Key Encipherment') when 'extendedKeyUsage' usage = ex.value.split(/\s*,\s*/) server_auth = usage.include?('Netscape Server Gated Crypto') when 'nsCertType' usage = ex.value.split(/\s*,\s*/) ca = usage.include?('SSL CA') server_auth = usage.include?('SSL Server') end end if self_signed warn('self signing CA') if $DEBUG return true elsif ca warn('middle level CA') if $DEBUG return true elsif server_auth warn('for server authentication') if $DEBUG return true end return false end
def set_client_cert_file(cert_file, key_file, pass = nil)
use client_key=.
RSA key. If you want to use other PKey algorithm,
key_file:: must be a filename of PEM/DER formatted file. Key must be an
cert_file:: must be a filename of PEM/DER formatted file.
Sets certificate and private key for SSL client authentication.
def set_client_cert_file(cert_file, key_file, pass = nil) @client_cert = X509::Certificate.new(File.open(cert_file) { |f| f.read }) @client_key = PKey::RSA.new(File.open(key_file) { |f| f.read }, pass) change_notify end
def set_context(ctx) # :nodoc:
interfaces for SSLSocketWrap.
def set_context(ctx) # :nodoc: load_trust_ca unless @cacerts_loaded @cacerts_loaded = true # Verification: Use Store#verify_callback instead of SSLContext#verify*? ctx.cert_store = @cert_store ctx.verify_mode = @verify_mode ctx.verify_depth = @verify_depth if @verify_depth ctx.verify_callback = @verify_callback || method(:default_verify_callback) # SSL config ctx.cert = @client_cert ctx.key = @client_key ctx.client_ca = @client_ca ctx.timeout = @timeout ctx.options = @options ctx.ciphers = @ciphers ctx.ssl_version = @ssl_version unless @ssl_version == :auto end
def set_default_paths
SSL_CERT_DIR=/etc/ssl/certs ruby -rhttpclient -e "..."
CA certificates directory by SSL_CERT_DIR env variable at runtime.
On Windows or when you build OpenSSL manually, you can set the
does not work with Windows Certificate Storage.
/etc/pki/certs or /etc/ssl/certs. Unfortunately OpenSSL's Windows build
configured to use OS's trusted CA certificates located at
Sets OpenSSL's default trusted CA certificates. Generally, OpenSSL is
def set_default_paths @cacerts_loaded = true # avoid lazy override @cert_store = X509::Store.new @cert_store.set_default_paths change_notify end
def ssl_version=(ssl_version)
Sets SSL version method String. Possible values: "SSLv2" for SSL2,
def ssl_version=(ssl_version) @ssl_version = ssl_version change_notify end
def timeout=(timeout)
Sets SSL timeout in sec.
def timeout=(timeout) @timeout = timeout change_notify end
def verify_callback=(verify_callback)
See verify_callback.
Sets callback handler for custom certificate verification.
def verify_callback=(verify_callback) @verify_callback = verify_callback change_notify end
def verify_depth=(verify_depth)
Sets verify depth. New value must be a number.
def verify_depth=(verify_depth) @verify_depth = verify_depth change_notify end
def verify_mode=(verify_mode)
constants OpenSSL::SSL::VERIFY_*
Sets verify mode of OpenSSL. New value must be a combination of
def verify_mode=(verify_mode) @verify_mode = verify_mode change_notify end