class OpenSSL::SSL::SSLContext
SSLContext
用于设置关于证书、算法、验证、会话缓存等的各种选项。SSLContext
用于创建 SSLSocket
。
所有属性都必须在创建 SSLSocket
之前设置,因为 SSLContext
之后将被冻结。
常量
- DH_ffdhe2048
- METHODS
可用 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.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
-
certificate 的私钥。是
OpenSSL::PKey::PKey
的实例。 - extra_certs
-
可选。
OpenSSL::X509::Certificate
的数组。当发送证书链时,此项指定的证书会按照数组中的顺序在 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; }
删除内部缓存中在 time 过期的会话。
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 189 def max_version=(version) set_minmax_proto_version(@min_proto_version ||= nil, version) @max_proto_version = version end
设置支持的 SSL/TLS 协议版本的下限。该版本可以通过名为 OpenSSL::SSL::*_VERSION 的整数常量、一个 Symbol 或 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 177 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 选项。这些选项是一个位字段,可以使用按位 OR 运算符 (|
) 进行组合。可用的选项在 OpenSSL::SSL
中定义为以 OP_
开头的常量。
为了向后兼容,传递 nil
与传递 OpenSSL::SSL::OP_ALL 具有相同的效果。
另请参阅手册页 SSL_CTX_set_options(3)。
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 的密码套件或短于 1024 位的 RSA 密钥。
请注意,尝试设置此类安全性不足的参数也会被阻止。您需要先降低级别。
此功能在 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 145 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 208 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; }