类 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
服务器会话将添加到会话缓存中
属性
一个 Enumerable
的字符串。每个 String
代表一个协议,将作为应用程序层协议协商支持的协议列表进行广告。在 OpenSSL
1.0.2 及更高版本中受支持。对服务器端没有影响。如果未显式设置,则 ALPN 扩展将不会包含在握手过程中。
示例¶ ↑
ctx.alpn_protocols = ["http/1.1", "spdy/2", "h2"]
包含 PEM 格式 CA 证书的文件的路径
包含 PEM 格式 CA 证书的目录的路径。
通过主题的 X509
名称的哈希值查找文件。
上下文证书
cert、key 和 extra_chain_cert 属性已弃用。建议使用 add_certificate
代替。
用于证书验证的 OpenSSL::X509::Store
。
将发送给客户端的证书或 Array
证书。
当服务器请求客户端证书且未设置证书时调用的回调。
回调使用 Session
调用,并且必须返回一个包含 OpenSSL::X509::Certificate
和 OpenSSL::PKey
的 Array
。如果返回任何其他值,则握手将暂停。
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
一个 Enumerable
的字符串。每个 String
代表一个协议,将作为下一协议协商支持的协议列表进行广告。在 OpenSSL
1.0.1 及更高版本中受支持。对客户端端没有影响。如果未显式设置,则服务器在握手过程中不会发送 NPN 扩展。
示例¶ ↑
ctx.npn_protocols = ["http/1.1", "spdy/2"]
在连接时调用的回调,以区分多个服务器名称。
回调使用 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 ext/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
-
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 ext/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 的整数常量、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 ext/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; }
返回包含以下键的 Hash
- :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 类似的协议的使用进行了优化。
如果给定 Hash
params,则参数将被其覆盖。params 中的键必须是 SSLContext
上的赋值方法。
如果 verify_mode
不是 VERIFY_NONE 并且 ca_file
、ca_path
和 cert_store
未设置,则使用系统默认证书存储。
# File ext/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 ext/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; }