类 OpenSSL::SSL::SSLContext
一个 SSLContext
用于设置有关证书、算法、验证、会话缓存等的各种选项。 SSLContext
用于创建 SSLSocket
.
所有属性必须在创建 SSLSocket
之前设置,因为 SSLContext
之后将被冻结。
常量
- DH_ffdhe2048
- 方法
可用的 SSL/TLS 方法列表。此常量仅为向后兼容性提供。
- METHODS_MAP
- SESSION_CACHE_BOTH
客户端和服务器会话都将添加到会话缓存中
- SESSION_CACHE_CLIENT
客户端会话将添加到会话缓存中
- SESSION_CACHE_NO_AUTO_CLEAR
通常,会话缓存会每 255 个连接检查一次过期会话。由于这可能会导致无法控制的延迟,因此可以禁用自动刷新,并可以显式调用
flush_sessions
。- SESSION_CACHE_NO_INTERNAL
同时启用
SESSION_CACHE_NO_INTERNAL_LOOKUP
和SESSION_CACHE_NO_INTERNAL_STORE
.- SESSION_CACHE_NO_INTERNAL_LOOKUP
即使会话在内部缓存中,也始终执行会话的外部查找。
此标志对客户端没有影响
- SESSION_CACHE_NO_INTERNAL_STORE
从不自动将会话存储在内部存储中。
- SESSION_CACHE_OFF
客户端或服务器没有会话缓存
- SESSION_CACHE_SERVER
服务器会话将添加到会话缓存中
属性
包含 PEM 格式 CA 证书的文件路径。
包含 PEM 格式 CA 证书的目录路径。
通过主题的 X509
名称的哈希值查找文件。
上下文证书
cert、key 和 extra_chain_cert 属性已弃用。建议使用 add_certificate
代替。
用于证书验证的 OpenSSL::X509::Store
。
将发送给客户端的证书或证书数组。
当服务器请求客户端证书且未设置证书时调用的回调函数。
回调函数使用 Session
调用,并且必须返回一个包含 OpenSSL::X509::Certificate
和 OpenSSL::PKey
的数组。如果返回任何其他值,则握手将暂停。
一个额外的 X509
证书数组,将被添加到证书链中。
cert、key 和 extra_chain_cert 属性已弃用。建议使用 add_certificate
代替。
上下文私钥
cert、key 和 extra_chain_cert 属性已弃用。建议使用 add_certificate
代替。
当 TLS 密钥材料生成或接收时调用的回调,以便应用程序可以将此密钥材料存储以供调试目的。
该回调将使用一个 SSLSocket
和一个字符串调用,该字符串包含 NSS 用于其 SSLKEYLOGFILE 调试输出的格式的密钥材料。
它仅与 OpenSSL
>= 1.1.1 兼容。即使 LibreSSL 从 v3.4.2 开始实现 SSL_CTX_set_keylog_callback(),它也不会执行任何操作(参见 github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6)。
示例¶ ↑
context.keylog_cb = proc do |_sock, line| File.open('ssl_keylog_file', "a") do |f| f.write("#{line}\n") end end
在连接时调用的回调,用于区分多个服务器名称。
回调使用一个 SSLSocket
和一个服务器名称进行调用。回调必须返回一个 SSLContext
用于服务器名称或 nil。
设置可以重用会话的上下文。这允许区分多个应用程序的会话,例如,通过名称。
在协商新会话时调用的回调。
回调使用一个 SSLSocket
进行调用。如果返回 false
,则会话将从内部缓存中删除。
在会话从内部缓存中删除时调用的回调。
回调使用一个 SSLContext
和一个 Session
进行调用。
重要说明:目前无法在多线程应用程序中安全地使用它。回调是在全局锁内调用的,它可能会随机导致 Ruby 线程切换时死锁。
最大会话生存时间(以秒为单位)。
最大会话生存时间(以秒为单位)。
在需要 DH 参数进行短暂 DH 密钥交换时调用的回调。
回调使用 SSLSocket
、一个指示使用导出密码的标志和所需的密钥长度进行调用。
回调必须返回一个 OpenSSL::PKey::DH
实例,其密钥长度正确。
在 3.0 版本中已弃用。 请使用 tmp_dh=
代替。
用于额外证书验证的回调函数。该回调函数会针对链中的每个证书调用。
回调函数会传入两个值。preverify_ok 指示验证是否通过(true
)或未通过(false
)。store_context 是一个 OpenSSL::X509::StoreContext
,包含用于证书验证的上下文。
如果回调函数返回 false
,则链验证会立即停止,并发送 bad_certificate 警告。
验证证书链时要遍历的 CA 证书数量。
是否检查服务器证书是否对主机名有效。
为了使此功能生效,verify_mode
必须设置为 VERIFY_PEER,并且服务器主机名必须通过 OpenSSL::SSL::SSLSocket#hostname=
提供。
Session
验证模式。
有效模式为 VERIFY_NONE、VERIFY_PEER、VERIFY_CLIENT_ONCE、VERIFY_FAIL_IF_NO_PEER_CERT,这些模式在 OpenSSL::SSL
中定义。
默认模式为 VERIFY_NONE,它不会执行任何验证。
有关详细信息,请参阅 SSL_CTX_set_verify(3)。
公共类方法
创建一个新的 SSL
上下文。
如果提供了参数,则 ssl_version=
会使用该值进行调用。请注意,此形式已弃用。新的应用程序应根据需要使用 min_version=
和 max_version=
。
# File openssl/lib/openssl/ssl.rb, line 127 def initialize(version = nil) self.options |= OpenSSL::SSL::OP_ALL self.ssl_version = version if version self.verify_mode = OpenSSL::SSL::VERIFY_NONE self.verify_hostname = false end
公共实例方法
向上下文添加证书。pkey 必须是与 certificate 相对应的私钥。
可以通过重复调用此方法添加具有不同公钥类型的多个证书,OpenSSL
会在握手过程中选择最合适的证书。
cert=
、key=
和 extra_chain_cert=
是用于设置证书的旧访问器方法,它们会在内部调用此方法。
参数¶ ↑
- certificate
-
证书。一个
OpenSSL::X509::Certificate
实例。 - pkey
-
证书的私钥。一个
OpenSSL::PKey::PKey
实例。 - extra_certs
-
可选。一个
OpenSSL::X509::Certificate
数组。当发送证书链时,由该参数指定的证书将按照数组中的顺序,在证书之后发送。
示例¶ ↑
rsa_cert = OpenSSL::X509::Certificate.new(...) rsa_pkey = OpenSSL::PKey.read(...) ca_intermediate_cert = OpenSSL::X509::Certificate.new(...) ctx.add_certificate(rsa_cert, rsa_pkey, [ca_intermediate_cert]) ecdsa_cert = ... ecdsa_pkey = ... another_ca_cert = ... ctx.add_certificate(ecdsa_cert, ecdsa_pkey, [another_ca_cert])
static VALUE ossl_sslctx_add_certificate(int argc, VALUE *argv, VALUE self) { VALUE cert, key, extra_chain_ary; SSL_CTX *ctx; X509 *x509; STACK_OF(X509) *extra_chain = NULL; EVP_PKEY *pkey, *pub_pkey; GetSSLCTX(self, ctx); rb_scan_args(argc, argv, "21", &cert, &key, &extra_chain_ary); rb_check_frozen(self); x509 = GetX509CertPtr(cert); pkey = GetPrivPKeyPtr(key); /* * The reference counter is bumped, and decremented immediately. * X509_get0_pubkey() is only available in OpenSSL >= 1.1.0. */ pub_pkey = X509_get_pubkey(x509); EVP_PKEY_free(pub_pkey); if (!pub_pkey) rb_raise(rb_eArgError, "certificate does not contain public key"); if (EVP_PKEY_eq(pub_pkey, pkey) != 1) rb_raise(rb_eArgError, "public key mismatch"); if (argc >= 3) extra_chain = ossl_x509_ary2sk(extra_chain_ary); if (!SSL_CTX_use_certificate(ctx, x509)) { sk_X509_pop_free(extra_chain, X509_free); ossl_raise(eSSLError, "SSL_CTX_use_certificate"); } if (!SSL_CTX_use_PrivateKey(ctx, pkey)) { sk_X509_pop_free(extra_chain, X509_free); ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey"); } if (extra_chain && !SSL_CTX_set0_chain(ctx, extra_chain)) { sk_X509_pop_free(extra_chain, X509_free); ossl_raise(eSSLError, "SSL_CTX_set0_chain"); } return self; }
此上下文配置的密码套件列表。
static VALUE ossl_sslctx_get_ciphers(VALUE self) { SSL_CTX *ctx; STACK_OF(SSL_CIPHER) *ciphers; const SSL_CIPHER *cipher; VALUE ary; int i, num; GetSSLCTX(self, ctx); ciphers = SSL_CTX_get_ciphers(ctx); if (!ciphers) return rb_ary_new(); num = sk_SSL_CIPHER_num(ciphers); ary = rb_ary_new2(num); for(i = 0; i < num; i++){ cipher = sk_SSL_CIPHER_value(ciphers, i); rb_ary_push(ary, ossl_ssl_cipher_to_ary(cipher)); } return ary; }
设置此上下文可用的密码套件列表。请注意,在服务器上下文中,某些密码套件需要相应的证书。例如,只有在 RSA 证书可用时才能选择 RSA 密码套件。
static VALUE ossl_sslctx_set_ciphers(VALUE self, VALUE v) { SSL_CTX *ctx; VALUE str; rb_check_frozen(self); if (NIL_P(v)) return v; str = build_cipher_string(v); GetSSLCTX(self, ctx); if (!SSL_CTX_set_cipher_list(ctx, StringValueCStr(str))) ossl_raise(eSSLError, "SSL_CTX_set_cipher_list"); return v; }
设置此上下文可用的 TLSv1.3 密码套件列表。
static VALUE ossl_sslctx_set_ciphersuites(VALUE self, VALUE v) { SSL_CTX *ctx; VALUE str; rb_check_frozen(self); if (NIL_P(v)) return v; str = build_cipher_string(v); GetSSLCTX(self, ctx); if (!SSL_CTX_set_ciphersuites(ctx, StringValueCStr(str))) ossl_raise(eSSLError, "SSL_CTX_set_ciphersuites"); return v; }
设置此上下文的“支持的椭圆曲线”列表。
对于 TLS 客户端,该列表直接用于支持的椭圆曲线扩展。对于服务器,该列表由 OpenSSL
用于确定共享曲线的集合。 OpenSSL
将从中选择最合适的曲线。
示例¶ ↑
ctx1 = OpenSSL::SSL::SSLContext.new ctx1.ecdh_curves = "X25519:P-256:P-224" svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx1) Thread.new { svr.accept } ctx2 = OpenSSL::SSL::SSLContext.new ctx2.ecdh_curves = "P-256" cli = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx2) cli.connect p cli.tmp_key.group.curve_name # => "prime256v1" (is an alias for NIST P-256)
static VALUE ossl_sslctx_set_ecdh_curves(VALUE self, VALUE arg) { SSL_CTX *ctx; rb_check_frozen(self); GetSSLCTX(self, ctx); StringValueCStr(arg); if (!SSL_CTX_set1_curves_list(ctx, RSTRING_PTR(arg))) ossl_raise(eSSLError, NULL); return arg; }
为此上下文激活 TLS_FALLBACK_SCSV。参见 RFC 7507。
static VALUE ossl_sslctx_enable_fallback_scsv(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); SSL_CTX_set_mode(ctx, SSL_MODE_SEND_FALLBACK_SCSV); return Qnil; }
删除内部缓存中在时间之前过期的会话。
static VALUE ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self) { VALUE arg1; SSL_CTX *ctx; time_t tm = 0; rb_scan_args(argc, argv, "01", &arg1); GetSSLCTX(self, ctx); if (NIL_P(arg1)) { tm = time(0); } else if (rb_obj_is_instance_of(arg1, rb_cTime)) { tm = NUM2LONG(rb_funcall(arg1, rb_intern("to_i"), 0)); } else { ossl_raise(rb_eArgError, "arg must be Time or nil"); } SSL_CTX_flush_sessions(ctx, (long)tm); return self; }
设置支持的 SSL/TLS 协议版本的上限。有关可能的值,请参见 min_version=
。
# File openssl/lib/openssl/ssl.rb, line 190 def max_version=(version) set_minmax_proto_version(@min_proto_version ||= nil, version) @max_proto_version = version end
设置支持的 SSL/TLS 协议版本的下限。版本可以通过名为 OpenSSL::SSL::*_VERSION 的整数常量、符号或 nil
指定,nil
表示“任何版本”。
请注意,在调用 min_version=
或 max_version=
之后,不要通过 options=
覆盖 OpenSSL::SSL::OP_NO_{SSL,TLS}v* 选项。
示例¶ ↑
ctx = OpenSSL::SSL::SSLContext.new ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx) sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
# File openssl/lib/openssl/ssl.rb, line 178 def min_version=(version) set_minmax_proto_version(version, @max_proto_version ||= nil) @min_proto_version = version end
获取各种 OpenSSL
选项。
static VALUE ossl_sslctx_get_options(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); /* * Do explicit cast because SSL_CTX_get_options() returned (signed) long in * OpenSSL before 1.1.0. */ return ULONG2NUM((unsigned long)SSL_CTX_get_options(ctx)); }
设置各种 OpenSSL
选项。
static VALUE ossl_sslctx_set_options(VALUE self, VALUE options) { SSL_CTX *ctx; rb_check_frozen(self); GetSSLCTX(self, ctx); SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx)); if (NIL_P(options)) { SSL_CTX_set_options(ctx, SSL_OP_ALL); } else { SSL_CTX_set_options(ctx, NUM2ULONG(options)); } return self; }
返回上下文的安全级别。
另请参见 OpenSSL::SSL::SSLContext#security_level=
。
static VALUE ossl_sslctx_get_security_level(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); #if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL) return INT2NUM(SSL_CTX_get_security_level(ctx)); #else (void)ctx; return INT2FIX(0); #endif }
设置上下文的安全级别。 OpenSSL
根据级别限制参数。“参数”包括:密码套件、曲线、密钥大小、证书签名算法、协议版本等等。例如,级别 1 拒绝提供低于 80 位安全性的参数,例如使用 MD5 进行 MAC 或 RSA 密钥短于 1024 位的密码套件。
请注意,尝试设置安全级别不足的此类参数也会被阻止。您需要先降低级别。
此功能在 OpenSSL
< 1.1.0 中不受支持,将级别设置为 0 以外的值将引发 NotImplementedError。级别 0 表示允许所有操作,与 OpenSSL
的先前版本的行为相同。
有关详细信息,请参见 SSL_CTX_set_security_level(3) 的手册页。
static VALUE ossl_sslctx_set_security_level(VALUE self, VALUE value) { SSL_CTX *ctx; rb_check_frozen(self); GetSSLCTX(self, ctx); #if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL) SSL_CTX_set_security_level(ctx, NUM2INT(value)); #else (void)ctx; if (NUM2INT(value) != 0) ossl_raise(rb_eNotImpError, "setting security level to other than 0 is " "not supported in this version of OpenSSL"); #endif return value; }
将 session 添加到会话缓存。
static VALUE ossl_sslctx_session_add(VALUE self, VALUE arg) { SSL_CTX *ctx; SSL_SESSION *sess; GetSSLCTX(self, ctx); GetSSLSession(arg, sess); return SSL_CTX_add_session(ctx, sess) == 1 ? Qtrue : Qfalse; }
当前会话缓存模式。
static VALUE ossl_sslctx_get_session_cache_mode(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); return LONG2NUM(SSL_CTX_get_session_cache_mode(ctx)); }
设置 SSL
会话缓存模式。按位或运算所需 SESSION_CACHE_* 常量以设置。有关详细信息,请参见 SSL_CTX_set_session_cache_mode(3)。
static VALUE ossl_sslctx_set_session_cache_mode(VALUE self, VALUE arg) { SSL_CTX *ctx; GetSSLCTX(self, ctx); SSL_CTX_set_session_cache_mode(ctx, NUM2LONG(arg)); return arg; }
返回当前会话缓存大小。零表示无限缓存大小。
static VALUE ossl_sslctx_get_session_cache_size(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); return LONG2NUM(SSL_CTX_sess_get_cache_size(ctx)); }
设置会话缓存大小。返回之前有效的会话缓存大小。零表示无限会话缓存大小。
static VALUE ossl_sslctx_set_session_cache_size(VALUE self, VALUE arg) { SSL_CTX *ctx; GetSSLCTX(self, ctx); SSL_CTX_sess_set_cache_size(ctx, NUM2LONG(arg)); return arg; }
返回一个包含以下键的哈希表:
- :accept
-
服务器模式下启动的 SSL/TLS 握手次数
- :accept_good
-
服务器模式下建立的 SSL/TLS 会话数
- :accept_renegotiate
-
服务器模式下启动重新协商的次数
- :cache_full
-
由于缓存溢出而删除的会话数
- :cache_hits
-
成功重用连接的次数
- :cache_misses
-
客户端提出的但在缓存中找不到的会话数
- :cache_num
-
内部会话缓存中的会话数
- :cb_hits
-
从服务器模式下的外部缓存中检索到的会话数
- :connect
-
客户端模式下启动的 SSL/TLS 握手次数
- :connect_good
-
客户端模式下建立的 SSL/TLS 会话数
- :connect_renegotiate
-
客户端模式下启动重新协商的次数
- :timeouts
-
客户端提出的但在缓存中找到但由于超时而过期的会话数
static VALUE ossl_sslctx_get_session_cache_stats(VALUE self) { SSL_CTX *ctx; VALUE hash; GetSSLCTX(self, ctx); hash = rb_hash_new(); rb_hash_aset(hash, ID2SYM(rb_intern("cache_num")), LONG2NUM(SSL_CTX_sess_number(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("connect")), LONG2NUM(SSL_CTX_sess_connect(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("connect_good")), LONG2NUM(SSL_CTX_sess_connect_good(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("connect_renegotiate")), LONG2NUM(SSL_CTX_sess_connect_renegotiate(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("accept")), LONG2NUM(SSL_CTX_sess_accept(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("accept_good")), LONG2NUM(SSL_CTX_sess_accept_good(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("accept_renegotiate")), LONG2NUM(SSL_CTX_sess_accept_renegotiate(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("cache_hits")), LONG2NUM(SSL_CTX_sess_hits(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("cb_hits")), LONG2NUM(SSL_CTX_sess_cb_hits(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("cache_misses")), LONG2NUM(SSL_CTX_sess_misses(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("cache_full")), LONG2NUM(SSL_CTX_sess_cache_full(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("timeouts")), LONG2NUM(SSL_CTX_sess_timeouts(ctx))); return hash; }
从会话缓存中删除session。
static VALUE ossl_sslctx_session_remove(VALUE self, VALUE arg) { SSL_CTX *ctx; SSL_SESSION *sess; GetSSLCTX(self, ctx); GetSSLSession(arg, sess); return SSL_CTX_remove_session(ctx, sess) == 1 ? Qtrue : Qfalse; }
设置更合理的默认值,针对与 HTTP 类似的协议进行优化。
如果给定一个哈希表params,则参数将被它覆盖。params中的键必须是SSLContext
上的赋值方法。
如果verify_mode
不是 VERIFY_NONE 且ca_file
、ca_path
和cert_store
未设置,则使用系统默认证书存储。
# File openssl/lib/openssl/ssl.rb, line 146 def set_params(params={}) params = DEFAULT_PARAMS.merge(params) self.options = params.delete(:options) # set before min_version/max_version params.each{|name, value| self.__send__("#{name}=", value) } if self.verify_mode != OpenSSL::SSL::VERIFY_NONE unless self.ca_file or self.ca_path or self.cert_store self.cert_store = DEFAULT_CERT_STORE end end return params end
此方法在创建新的 SSLSocket
时会自动调用。但是,它不是线程安全的,必须在多线程程序中创建 SSLSocket
对象之前调用。
static VALUE ossl_sslctx_setup(VALUE self) { SSL_CTX *ctx; X509 *cert = NULL, *client_ca = NULL; EVP_PKEY *key = NULL; char *ca_path = NULL, *ca_file = NULL; int verify_mode; long i; VALUE val; if(OBJ_FROZEN(self)) return Qnil; GetSSLCTX(self, ctx); #if !defined(OPENSSL_NO_DH) SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback); #endif #ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH SSL_CTX_set_post_handshake_auth(ctx, 1); #endif val = rb_attr_get(self, id_i_cert_store); if (!NIL_P(val)) { X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */ SSL_CTX_set_cert_store(ctx, store); X509_STORE_up_ref(store); } val = rb_attr_get(self, id_i_extra_chain_cert); if(!NIL_P(val)){ rb_block_call(val, rb_intern("each"), 0, 0, ossl_sslctx_add_extra_chain_cert_i, self); } /* private key may be bundled in certificate file. */ val = rb_attr_get(self, id_i_cert); cert = NIL_P(val) ? NULL : GetX509CertPtr(val); /* NO DUP NEEDED */ val = rb_attr_get(self, id_i_key); key = NIL_P(val) ? NULL : GetPrivPKeyPtr(val); /* NO DUP NEEDED */ if (cert && key) { if (!SSL_CTX_use_certificate(ctx, cert)) { /* Adds a ref => Safe to FREE */ ossl_raise(eSSLError, "SSL_CTX_use_certificate"); } if (!SSL_CTX_use_PrivateKey(ctx, key)) { /* Adds a ref => Safe to FREE */ ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey"); } if (!SSL_CTX_check_private_key(ctx)) { ossl_raise(eSSLError, "SSL_CTX_check_private_key"); } } val = rb_attr_get(self, id_i_client_ca); if(!NIL_P(val)){ if (RB_TYPE_P(val, T_ARRAY)) { for(i = 0; i < RARRAY_LEN(val); i++){ client_ca = GetX509CertPtr(RARRAY_AREF(val, i)); if (!SSL_CTX_add_client_CA(ctx, client_ca)){ /* Copies X509_NAME => FREE it. */ ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); } } } else{ client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */ if (!SSL_CTX_add_client_CA(ctx, client_ca)){ /* Copies X509_NAME => FREE it. */ ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); } } } val = rb_attr_get(self, id_i_ca_file); ca_file = NIL_P(val) ? NULL : StringValueCStr(val); val = rb_attr_get(self, id_i_ca_path); ca_path = NIL_P(val) ? NULL : StringValueCStr(val); #ifdef HAVE_SSL_CTX_LOAD_VERIFY_FILE if (ca_file && !SSL_CTX_load_verify_file(ctx, ca_file)) ossl_raise(eSSLError, "SSL_CTX_load_verify_file"); if (ca_path && !SSL_CTX_load_verify_dir(ctx, ca_path)) ossl_raise(eSSLError, "SSL_CTX_load_verify_dir"); #else if (ca_file || ca_path) { if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path)) ossl_raise(eSSLError, "SSL_CTX_load_verify_locations"); } #endif val = rb_attr_get(self, id_i_verify_mode); verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val); SSL_CTX_set_verify(ctx, verify_mode, ossl_ssl_verify_callback); if (RTEST(rb_attr_get(self, id_i_client_cert_cb))) SSL_CTX_set_client_cert_cb(ctx, ossl_client_cert_cb); val = rb_attr_get(self, id_i_timeout); if(!NIL_P(val)) SSL_CTX_set_timeout(ctx, NUM2LONG(val)); val = rb_attr_get(self, id_i_verify_depth); if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val)); #ifdef OSSL_USE_NEXTPROTONEG val = rb_attr_get(self, id_i_npn_protocols); if (!NIL_P(val)) { VALUE encoded = ssl_encode_npn_protocols(val); rb_ivar_set(self, id_npn_protocols_encoded, encoded); SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)self); OSSL_Debug("SSL NPN advertise callback added"); } if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) { SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, (void *) self); OSSL_Debug("SSL NPN select callback added"); } #endif val = rb_attr_get(self, id_i_alpn_protocols); if (!NIL_P(val)) { VALUE rprotos = ssl_encode_npn_protocols(val); /* returns 0 on success */ if (SSL_CTX_set_alpn_protos(ctx, (unsigned char *)RSTRING_PTR(rprotos), RSTRING_LENINT(rprotos))) ossl_raise(eSSLError, "SSL_CTX_set_alpn_protos"); OSSL_Debug("SSL ALPN values added"); } if (RTEST(rb_attr_get(self, id_i_alpn_select_cb))) { SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self); OSSL_Debug("SSL ALPN select callback added"); } rb_obj_freeze(self); val = rb_attr_get(self, id_i_session_id_context); if (!NIL_P(val)){ StringValue(val); if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val))){ ossl_raise(eSSLError, "SSL_CTX_set_session_id_context"); } } if (RTEST(rb_attr_get(self, id_i_session_get_cb))) { SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb); OSSL_Debug("SSL SESSION get callback added"); } if (RTEST(rb_attr_get(self, id_i_session_new_cb))) { SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb); OSSL_Debug("SSL SESSION new callback added"); } if (RTEST(rb_attr_get(self, id_i_session_remove_cb))) { SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb); OSSL_Debug("SSL SESSION remove callback added"); } val = rb_attr_get(self, id_i_servername_cb); if (!NIL_P(val)) { SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); OSSL_Debug("SSL TLSEXT servername callback added"); } #if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER) /* * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6). */ if (RTEST(rb_attr_get(self, id_i_keylog_cb))) { SSL_CTX_set_keylog_callback(ctx, ossl_sslctx_keylog_cb); OSSL_Debug("SSL keylog callback added"); } #endif return Qtrue; }
设置上下文的 SSL/TLS 协议版本。这将强制连接仅使用指定的协议版本。此方法已弃用,仅为了向后兼容性而提供。请使用 min_version=
和 max_version=
代替。
历史记录¶ ↑
顾名思义,此方法以前调用 SSL_CTX_set_ssl_version() 函数,该函数设置用于从上下文中创建的连接的 SSL
方法。从 Ruby/OpenSSL 2.1 开始,此访问器方法被实现为调用 min_version=
和 max_version=
代替。
# File openssl/lib/openssl/ssl.rb, line 209 def ssl_version=(meth) meth = meth.to_s if meth.is_a?(Symbol) if /(?<type>_client|_server)\z/ =~ meth meth = $` if $VERBOSE warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored" end end version = METHODS_MAP[meth.intern] or raise ArgumentError, "unknown SSL method `%s'" % meth set_minmax_proto_version(version, version) @min_proto_version = @max_proto_version = version end
设置用于临时 DH 密钥交换的 DH 参数。这仅与服务器相关。
pkey
是 OpenSSL::PKey::DH
的实例。请注意,密钥对象中包含的密钥组件(如果有)将被忽略。服务器将始终为每次握手生成新的密钥对。
在版本 3.0 中添加。另请参阅手册页 SSL_set0_tmp_dh_pkey(3)。
示例
ctx = OpenSSL::SSL::SSLContext.new ctx.tmp_dh = OpenSSL::DH.generate(2048) svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx) Thread.new { svr.accept }
static VALUE ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg) { SSL_CTX *ctx; EVP_PKEY *pkey; rb_check_frozen(self); GetSSLCTX(self, ctx); pkey = GetPKeyPtr(arg); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) rb_raise(eSSLError, "invalid pkey type %s (expected DH)", OBJ_nid2sn(EVP_PKEY_base_id(pkey))); #ifdef HAVE_SSL_SET0_TMP_DH_PKEY if (!SSL_CTX_set0_tmp_dh_pkey(ctx, pkey)) ossl_raise(eSSLError, "SSL_CTX_set0_tmp_dh_pkey"); EVP_PKEY_up_ref(pkey); #else if (!SSL_CTX_set_tmp_dh(ctx, EVP_PKEY_get0_DH(pkey))) ossl_raise(eSSLError, "SSL_CTX_set_tmp_dh"); #endif return arg; }
私有实例方法
设置支持的最小和最大协议版本。请参阅 min_version=
和 max_version=
。
static VALUE ossl_sslctx_set_minmax_proto_version(VALUE self, VALUE min_v, VALUE max_v) { SSL_CTX *ctx; int min, max; GetSSLCTX(self, ctx); min = parse_proto_version(min_v); max = parse_proto_version(max_v); #ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION if (!SSL_CTX_set_min_proto_version(ctx, min)) ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version"); if (!SSL_CTX_set_max_proto_version(ctx, max)) ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version"); #else { unsigned long sum = 0, opts = 0; int i; static const struct { int ver; unsigned long opts; } options_map[] = { { SSL2_VERSION, SSL_OP_NO_SSLv2 }, { SSL3_VERSION, SSL_OP_NO_SSLv3 }, { TLS1_VERSION, SSL_OP_NO_TLSv1 }, { TLS1_1_VERSION, SSL_OP_NO_TLSv1_1 }, { TLS1_2_VERSION, SSL_OP_NO_TLSv1_2 }, # if defined(TLS1_3_VERSION) { TLS1_3_VERSION, SSL_OP_NO_TLSv1_3 }, # endif }; for (i = 0; i < numberof(options_map); i++) { sum |= options_map[i].opts; if ((min && min > options_map[i].ver) || (max && max < options_map[i].ver)) { opts |= options_map[i].opts; } } SSL_CTX_clear_options(ctx, sum); SSL_CTX_set_options(ctx, opts); } #endif return Qnil; }