Class: HTTPClient::SSLConfig
- Inherits:
-
Object
- Object
- HTTPClient::SSLConfig
- Defined in:
- lib/httpclient/ssl_config.rb
Overview
Represents SSL configuration for HTTPClient instance. The implementation depends on OpenSSL.
Trust Anchor Control
SSLConfig loads ‘httpclient/cacert.pem’ as a trust anchor (trusted certificate(s)) with add_trust_ca in initialization time. This means that HTTPClient instance trusts some CA certificates by default, like Web browsers. ‘httpclient/cacert.pem’ is downloaded from curl web site by the author and included in released package.
On JRuby, HTTPClient uses Java runtime’s trusted CA certificates, not cacert.pem by default. You can load cacert.pem by calling SSLConfig#load_trust_ca manually like:
HTTPClient.new { self.ssl_config.load_trust_ca }.get("https://...")
You may want to change trust anchor by yourself. Call clear_cert_store then add_trust_ca for that purpose.
Constant Summary collapse
- CIPHERS_DEFAULT =
OpenSSL >1.0.0 default
"ALL:!aNULL:!eNULL:!SSLv2"
Instance Attribute Summary collapse
-
#cert_store ⇒ Object
OpenSSL::X509::X509::Store used for verification.
-
#cert_store_crl_items ⇒ Object
readonly
Returns the value of attribute cert_store_crl_items.
Instance Method Summary collapse
-
#add_crl(crl) ⇒ Object
(also: #set_crl)
Adds CRL for verification.
-
#add_trust_ca(trust_ca_file_or_hashed_dir) ⇒ Object
(also: #set_trust_ca)
Sets trust anchor certificate(s) for verification.
- #add_trust_ca_to_store(cert_store, trust_ca_file_or_hashed_dir) ⇒ Object
-
#cert_store_items ⇒ Object
These array keeps original files/dirs that was added to @cert_store.
-
#clear_cert_store ⇒ Object
Drops current certificate store (OpenSSL::X509::Store) for SSL and create new one for the next session.
-
#default_verify_callback(is_ok, ctx) ⇒ Object
Default callback for verification: only dumps error.
-
#initialize(client) ⇒ SSLConfig
constructor
Creates a SSLConfig.
-
#load_trust_ca ⇒ Object
Loads default trust anchors.
-
#post_connection_check(peer_cert, hostname) ⇒ Object
post connection check proc for ruby < 1.8.5.
-
#sample_verify_callback(is_ok, ctx) ⇒ Object
Sample callback method: CAUTION: does not check CRL/ARL.
-
#set_client_cert_file(cert_file, key_file, pass = nil) ⇒ Object
Sets certificate and private key for SSL client authentication.
-
#set_context(ctx) ⇒ Object
interfaces for SSLSocket.
-
#set_default_paths ⇒ Object
Sets OpenSSL’s default trusted CA certificates.
- #verify? ⇒ Boolean
Methods included from Util
#argument_to_hash, hash_find_value, #http?, #https?, #keyword_argument, try_require, uri_dirname, uri_part_of, urify, #warning
Constructor Details
#initialize(client) ⇒ SSLConfig
Creates a SSLConfig.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/httpclient/ssl_config.rb', line 145 def initialize(client) return unless SSLEnabled @client = client @cert_store = X509::Store.new @cert_store_crl_items = [] @client_cert = @client_key = @client_key_pass = @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 = CIPHERS_DEFAULT @cacerts_loaded = false end |
Instance Attribute Details
#cert_store ⇒ Object
OpenSSL::X509::X509::Store used for verification. You can reset the store with clear_cert_store and set the new store with cert_store=.
135 136 137 |
# File 'lib/httpclient/ssl_config.rb', line 135 def cert_store @cert_store end |
#cert_store_crl_items ⇒ Object (readonly)
Returns the value of attribute cert_store_crl_items.
142 143 144 |
# File 'lib/httpclient/ssl_config.rb', line 142 def cert_store_crl_items @cert_store_crl_items end |
Instance Method Details
#add_crl(crl) ⇒ Object Also known as: set_crl
Adds CRL for verification.
- crl
-
a OpenSSL::X509::CRL or a filename of a PEM/DER formatted OpenSSL::X509::CRL.
On JRuby, instead of setting CRL by yourself you can set following options to let HTTPClient to perform revocation check with CRL and OCSP: -J-Dcom.sun.security.enableCRLDP=true -J-Dcom.sun.net.ssl.checkRevocation=true ex. jruby -J-Dcom.sun.security.enableCRLDP=true -J-Dcom.sun.net.ssl.checkRevocation=true app.rb
Revoked cert example: test-sspev.verisign.com:2443/test-SSPEV-revoked-verisign.html
Calling this method resets all existing sessions.
268 269 270 271 272 273 274 275 276 |
# File 'lib/httpclient/ssl_config.rb', line 268 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_crl_items << crl @cert_store.flags = X509::V_FLAG_CRL_CHECK | X509::V_FLAG_CRL_CHECK_ALL change_notify end |
#add_trust_ca(trust_ca_file_or_hashed_dir) ⇒ Object Also known as: set_trust_ca
Sets trust anchor certificate(s) for verification.
- trust_ca_file_or_hashed_dir
-
a filename of a PEM/DER formatted OpenSSL::X509::Certificate or a ‘c-rehash’eddirectory name which stores trusted certificate files.
Calling this method resets all existing sessions.
231 232 233 234 235 236 237 238 |
# File 'lib/httpclient/ssl_config.rb', line 231 def add_trust_ca(trust_ca_file_or_hashed_dir) unless File.exist?(trust_ca_file_or_hashed_dir) trust_ca_file_or_hashed_dir = File.join(File.dirname(__FILE__), trust_ca_file_or_hashed_dir) end @cacerts_loaded = true # avoid lazy override add_trust_ca_to_store(@cert_store, trust_ca_file_or_hashed_dir) change_notify end |
#add_trust_ca_to_store(cert_store, trust_ca_file_or_hashed_dir) ⇒ Object
241 242 243 244 245 246 247 |
# File 'lib/httpclient/ssl_config.rb', line 241 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 |
#cert_store_items ⇒ Object
These array keeps original files/dirs that was added to @cert_store
141 |
# File 'lib/httpclient/ssl_config.rb', line 141 def cert_store_items; @cert_store._httpclient_cert_store_items; end |
#clear_cert_store ⇒ Object
Drops current certificate store (OpenSSL::X509::Store) for SSL and create new one for the next session.
Calling this method resets all existing sessions.
204 205 206 207 208 209 |
# File 'lib/httpclient/ssl_config.rb', line 204 def clear_cert_store @cacerts_loaded = true # avoid lazy override @cert_store = X509::Store.new @cert_store._httpclient_cert_store_items.clear change_notify end |
#default_verify_callback(is_ok, ctx) ⇒ Object
Default callback for verification: only dumps error.
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/httpclient/ssl_config.rb', line 338 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 |
#load_trust_ca ⇒ Object
Loads default trust anchors. Calling this method resets all existing sessions.
251 252 253 254 |
# File 'lib/httpclient/ssl_config.rb', line 251 def load_trust_ca load_cacerts(@cert_store) change_notify end |
#post_connection_check(peer_cert, hostname) ⇒ Object
post connection check proc for ruby < 1.8.5. this definition must match with the one in ext/openssl/lib/openssl/ssl.rb
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/httpclient/ssl_config.rb', line 310 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 |
#sample_verify_callback(is_ok, ctx) ⇒ Object
Sample callback method: CAUTION: does not check CRL/ARL.
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'lib/httpclient/ssl_config.rb', line 358 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 |
#set_client_cert_file(cert_file, key_file, pass = nil) ⇒ Object
Sets certificate and private key for SSL client authentication.
- cert_file
-
must be a filename of PEM/DER formatted file.
- key_file
-
must be a filename of PEM/DER formatted file. Key must be an RSA key. If you want to use other PKey algorithm, use client_key=.
Calling this method resets all existing sessions if value is changed.
175 176 177 178 179 180 |
# File 'lib/httpclient/ssl_config.rb', line 175 def set_client_cert_file(cert_file, key_file, pass = nil) if (@client_cert != cert_file) || (@client_key != key_file) || (@client_key_pass != pass) @client_cert, @client_key, @client_key_pass = cert_file, key_file, pass change_notify end end |
#set_context(ctx) ⇒ Object
interfaces for SSLSocket.
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/httpclient/ssl_config.rb', line 284 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 if @client_cert ctx.cert = @client_cert.is_a?(X509::Certificate) ? @client_cert : X509::Certificate.new(File.open(@client_cert) { |f| f.read }) end if @client_key ctx.key = @client_key.is_a?(PKey::PKey) ? @client_key : PKey::RSA.new(File.open(@client_key) { |f| f.read }, @client_key_pass) end ctx.client_ca = @client_ca ctx.timeout = @timeout ctx. = @options ctx.ciphers = @ciphers ctx.ssl_version = @ssl_version unless @ssl_version == :auto end |
#set_default_paths ⇒ Object
Sets OpenSSL’s default trusted CA certificates. Generally, OpenSSL is configured to use OS’s trusted CA certificates located at /etc/pki/certs or /etc/ssl/certs. Unfortunately OpenSSL’s Windows build does not work with Windows Certificate Storage.
On Windows or when you build OpenSSL manually, you can set the CA certificates directory by SSL_CERT_DIR env variable at runtime.
SSL_CERT_DIR=/etc/ssl/certs ruby -rhttpclient -e "..."
Calling this method resets all existing sessions.
193 194 195 196 197 198 |
# File 'lib/httpclient/ssl_config.rb', line 193 def set_default_paths @cacerts_loaded = true # avoid lazy override @cert_store = X509::Store.new @cert_store.set_default_paths change_notify end |
#verify? ⇒ Boolean
279 280 281 |
# File 'lib/httpclient/ssl_config.rb', line 279 def verify? @verify_mode && (@verify_mode & OpenSSL::SSL::VERIFY_PEER != 0) end |