类 OpenSSL::SSL::SSLSocket
属性
此连接中使用的 SSLContext
对象。
底层的 IO
对象。
当 SSL/TLS 连接关闭时,是否也关闭底层套接字。默认为 false
。
底层的 IO
对象。
公共类方法
从 io 创建一个新的 SSL
套接字,io 必须是真正的 IO
对象(而不是响应 read/write 的 IO 类对象)。
如果提供了 ctx,则 SSL
套接字的初始参数将从上下文获取。
OpenSSL::Buffering
模块提供了额外的 IO
方法。
此方法将冻结 SSLContext
(如果提供了的话);但是,在冻结的 SSLContext
中仍然允许会话管理。
static VALUE ossl_ssl_initialize(int argc, VALUE *argv, VALUE self) { VALUE io, v_ctx, verify_cb; SSL *ssl; SSL_CTX *ctx; TypedData_Get_Struct(self, SSL, &ossl_ssl_type, ssl); if (ssl) ossl_raise(eSSLError, "SSL already initialized"); if (rb_scan_args(argc, argv, "11", &io, &v_ctx) == 1) v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0); GetSSLCTX(v_ctx, ctx); rb_ivar_set(self, id_i_context, v_ctx); ossl_sslctx_setup(v_ctx); if (rb_respond_to(io, rb_intern("nonblock="))) rb_funcall(io, rb_intern("nonblock="), 1, Qtrue); Check_Type(io, T_FILE); rb_ivar_set(self, id_i_io, io); ssl = SSL_new(ctx); if (!ssl) ossl_raise(eSSLError, NULL); RTYPEDDATA_DATA(self) = ssl; SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void *)self); SSL_set_info_callback(ssl, ssl_info_cb); verify_cb = rb_attr_get(v_ctx, id_i_verify_callback); // We don't need to trigger a write barrier because it's already // an instance variable of this object. SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)verify_cb); rb_call_super(0, NULL); return self; }
私有类方法
创建一个新的 SSLSocket
实例。remotehost_ 和 remoteport_ 用于打开 TCPSocket
。如果指定了 localhost_ 和 localport_,则这些参数将用于本地端建立连接。如果提供了 context,则 SSL
套接字的初始参数将从上下文获取。
示例¶ ↑
sock = OpenSSL::SSL::SSLSocket.open('localhost', 443) sock.connect # Initiates a connection to localhost:443
使用 SSLContext
ctx = OpenSSL::SSL::SSLContext.new sock = OpenSSL::SSL::SSLSocket.open('localhost', 443, context: ctx) sock.connect # Initiates a connection to localhost:443 with SSLContext
# File ext/openssl/lib/openssl/ssl.rb, line 470 def open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil) sock = ::TCPSocket.open(remote_host, remote_port, local_host, local_port) if context.nil? return OpenSSL::SSL::SSLSocket.new(sock) else return OpenSSL::SSL::SSLSocket.new(sock, context) end end
公共实例方法
等待 SSL/TLS 客户端发起握手。
static VALUE ossl_ssl_accept(VALUE self) { ossl_ssl_setup(self); return ossl_start_ssl(self, SSL_accept, "SSL_accept", Qfalse); }
以非阻塞方式作为服务器发起 SSL/TLS 握手。
# emulates blocking accept begin ssl.accept_nonblock rescue IO::WaitReadable IO.select([s2]) retry rescue IO::WaitWritable IO.select(nil, [s2]) retry end
通过将关键字参数 exception 指定为 false
,您可以指示 accept_nonblock
不应引发 IO::WaitReadable
或 IO::WaitWritable
异常,而是返回符号 :wait_readable
或 :wait_writable
。
static VALUE ossl_ssl_accept_nonblock(int argc, VALUE *argv, VALUE self) { VALUE opts; rb_scan_args(argc, argv, "0:", &opts); ossl_ssl_setup(self); return ossl_start_ssl(self, SSL_accept, "SSL_accept", opts); }
返回在握手期间服务器最终选择的 ALPN 协议字符串。
static VALUE ossl_ssl_alpn_protocol(VALUE self) { SSL *ssl; const unsigned char *out; unsigned int outlen; GetSSL(self, ssl); SSL_get0_alpn_selected(ssl, &out, &outlen); if (!outlen) return Qnil; else return rb_str_new((const char *) out, outlen); }
此套接字端点的 X509
证书。
static VALUE ossl_ssl_get_cert(VALUE self) { SSL *ssl; X509 *cert = NULL; GetSSL(self, ssl); /* * Is this OpenSSL bug? Should add a ref? * TODO: Ask for. */ cert = SSL_get_certificate(ssl); /* NO DUPs => DON'T FREE. */ if (!cert) { return Qnil; } return ossl_x509_new(cert); }
返回当前会话中实际使用的密码套件,如果尚未建立会话,则返回 nil。
static VALUE ossl_ssl_get_cipher(VALUE self) { SSL *ssl; const SSL_CIPHER *cipher; GetSSL(self, ssl); cipher = SSL_get_current_cipher(ssl); return cipher ? ossl_ssl_cipher_to_ary(cipher) : Qnil; }
返回客户端 CA 列表。请注意,与 SSLContext#client_ca=
相比,它不返回 X509::Certificate
数组,而是返回 CA 的主体区分名称的 X509::Name
实例。
在服务器模式下,返回由 SSLContext#client_ca=
设置的列表。在客户端模式下,返回服务器发送的客户端 CA 列表。
static VALUE ossl_ssl_get_client_ca_list(VALUE self) { SSL *ssl; STACK_OF(X509_NAME) *ca; GetSSL(self, ssl); ca = SSL_get_client_CA_list(ssl); return ossl_x509name_sk2ary(ca); }
与服务器发起 SSL/TLS 握手。
static VALUE ossl_ssl_connect(VALUE self) { ossl_ssl_setup(self); return ossl_start_ssl(self, SSL_connect, "SSL_connect", Qfalse); }
以非阻塞方式作为客户端发起 SSL/TLS 握手。
# emulates blocking connect begin ssl.connect_nonblock rescue IO::WaitReadable IO.select([s2]) retry rescue IO::WaitWritable IO.select(nil, [s2]) retry end
通过将关键字参数 exception 指定为 false
,您可以指示 connect_nonblock
不应引发 IO::WaitReadable
或 IO::WaitWritable
异常,而是返回符号 :wait_readable
或 :wait_writable
。
static VALUE ossl_ssl_connect_nonblock(int argc, VALUE *argv, VALUE self) { VALUE opts; rb_scan_args(argc, argv, "0:", &opts); ossl_ssl_setup(self); return ossl_start_ssl(self, SSL_connect, "SSL_connect", opts); }
根据 RFC 5705 启用共享会话密钥材料的使用。
static VALUE ossl_ssl_export_keying_material(int argc, VALUE *argv, VALUE self) { SSL *ssl; VALUE str; VALUE label; VALUE length; VALUE context; unsigned char *p; size_t len; int use_ctx = 0; unsigned char *ctx = NULL; size_t ctx_len = 0; int ret; rb_scan_args(argc, argv, "21", &label, &length, &context); StringValue(label); GetSSL(self, ssl); len = (size_t)NUM2LONG(length); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (!NIL_P(context)) { use_ctx = 1; StringValue(context); ctx = (unsigned char *)RSTRING_PTR(context); ctx_len = RSTRING_LEN(context); } ret = SSL_export_keying_material(ssl, p, len, (char *)RSTRING_PTR(label), RSTRING_LENINT(label), ctx, ctx_len, use_ctx); if (ret == 0 || ret == -1) { ossl_raise(eSSLError, "SSL_export_keying_material"); } return str; }
返回最后发送的 Finished 消息
static VALUE ossl_ssl_get_finished(VALUE self) { SSL *ssl; char sizer[1], *buf; size_t len; GetSSL(self, ssl); len = SSL_get_finished(ssl, sizer, 0); if (len == 0) return Qnil; buf = ALLOCA_N(char, len); SSL_get_finished(ssl, buf, len); return rb_str_new(buf, len); }
设置用于 SNI 的服务器主机名。这需要在 SSLSocket#connect
之前设置。
static VALUE ossl_ssl_set_hostname(VALUE self, VALUE arg) { SSL *ssl; char *hostname = NULL; GetSSL(self, ssl); if (!NIL_P(arg)) hostname = StringValueCStr(arg); if (!SSL_set_tlsext_host_name(ssl, hostname)) ossl_raise(eSSLError, NULL); /* for SSLSocket#hostname */ rb_ivar_set(self, id_i_hostname, arg); return arg; }
返回在握手期间客户端最终选择的协议字符串。
static VALUE ossl_ssl_npn_protocol(VALUE self) { SSL *ssl; const unsigned char *out; unsigned int outlen; GetSSL(self, ssl); SSL_get0_next_proto_negotiated(ssl, &out, &outlen); if (!outlen) return Qnil; else return rb_str_new((const char *) out, outlen); }
此套接字的对端的 X509
证书。
static VALUE ossl_ssl_get_peer_cert(VALUE self) { SSL *ssl; X509 *cert = NULL; VALUE obj; GetSSL(self, ssl); cert = SSL_get_peer_certificate(ssl); /* Adds a ref => Safe to FREE. */ if (!cert) { return Qnil; } obj = ossl_x509_new(cert); X509_free(cert); return obj; }
此套接字的对端的 X509
证书链。
static VALUE ossl_ssl_get_peer_cert_chain(VALUE self) { SSL *ssl; STACK_OF(X509) *chain; X509 *cert; VALUE ary; int i, num; GetSSL(self, ssl); chain = SSL_get_peer_cert_chain(ssl); if(!chain) return Qnil; num = sk_X509_num(chain); ary = rb_ary_new2(num); for (i = 0; i < num; i++){ cert = sk_X509_value(chain, i); rb_ary_push(ary, ossl_x509_new(cert)); } return ary; }
返回最后接收到的 Finished 消息
static VALUE ossl_ssl_get_peer_finished(VALUE self) { SSL *ssl; char sizer[1], *buf; size_t len; GetSSL(self, ssl); len = SSL_get_peer_finished(ssl, sizer, 0); if (len == 0) return Qnil; buf = ALLOCA_N(char, len); SSL_get_peer_finished(ssl, buf, len); return rb_str_new(buf, len); }
立即可用于读取的字节数。
static VALUE ossl_ssl_pending(VALUE self) { SSL *ssl; GetSSL(self, ssl); return INT2NUM(SSL_pending(ssl)); }
根据 RFC 6125 执行主机名验证。
此方法必须在调用 connect
后调用,以确保已验证远程对端的主机名。
# File ext/openssl/lib/openssl/ssl.rb, line 397 def post_connection_check(hostname) if peer_cert.nil? msg = "Peer verification enabled, but no certificate received." if using_anon_cipher? msg += " Anonymous cipher suite #{cipher[0]} was negotiated. " \ "Anonymous suites must be disabled to use peer verification." end raise SSLError, msg end unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname) raise SSLError, "hostname \"#{hostname}\" does not match the server certificate" end return true end
返回当前使用的 SSLSession 对象,如果尚未建立会话,则返回 nil。
# File ext/openssl/lib/openssl/ssl.rb, line 418 def session SSL::Session.new(self) rescue SSL::Session::SessionError nil end
设置在建立连接时要使用的 Session
。
static VALUE ossl_ssl_set_session(VALUE self, VALUE arg1) { SSL *ssl; SSL_SESSION *sess; GetSSL(self, ssl); GetSSLSession(arg1, sess); if (SSL_set_session(ssl, sess) != 1) ossl_raise(eSSLError, "SSL_set_session"); return arg1; }
如果在握手期间协商了重用会话,则返回 true
。
static VALUE ossl_ssl_session_reused(VALUE self) { SSL *ssl; GetSSL(self, ssl); return SSL_session_reused(ssl) ? Qtrue : Qfalse; }
返回一个 String
,表示为连接协商的 SSL/TLS 版本,例如“TLSv1.2”。
static VALUE ossl_ssl_get_version(VALUE self) { SSL *ssl; GetSSL(self, ssl); return rb_str_new2(SSL_get_version(ssl)); }
当前连接状态的描述。这仅用于诊断目的。
static VALUE ossl_ssl_get_state(VALUE self) { SSL *ssl; VALUE ret; GetSSL(self, ssl); ret = rb_str_new2(SSL_state_string(ssl)); if (ruby_verbose) { rb_str_cat2(ret, ": "); rb_str_cat2(ret, SSL_state_string_long(ssl)); } return ret; }
向对端发送“关闭通知”,并尝试优雅地关闭 SSL
连接。
如果 sync_close
设置为 true
,则也会关闭底层的 IO
。
# File ext/openssl/lib/openssl/ssl.rb, line 384 def sysclose return if closed? stop io.close if sync_close end
从 SSL
连接读取 length 字节。如果提供了预先分配的 buffer,则数据将写入其中。
static VALUE ossl_ssl_read(int argc, VALUE *argv, VALUE self) { return ossl_ssl_read_internal(argc, argv, self, 0); }
将 string 写入 SSL
连接。
static VALUE ossl_ssl_write(VALUE self, VALUE str) { return ossl_ssl_write_internal(self, str, Qfalse); }
返回在正向保密密码的情况下使用的临时密钥。
static VALUE ossl_ssl_tmp_key(VALUE self) { SSL *ssl; EVP_PKEY *key; GetSSL(self, ssl); if (!SSL_get_server_tmp_key(ssl, &key)) return Qnil; return ossl_pkey_new(key); }
返回对端证书验证的结果。有关错误值和描述,请参见 verify(1)。
如果未提供对端证书,则返回 X509_V_OK。
static VALUE ossl_ssl_get_verify_result(VALUE self) { SSL *ssl; GetSSL(self, ssl); return LONG2NUM(SSL_get_verify_result(ssl)); }
私有实例方法
# File ext/openssl/lib/openssl/ssl.rb, line 432 def client_cert_cb @context.client_cert_cb end
# File ext/openssl/lib/openssl/ssl.rb, line 444 def session_get_cb @context.session_get_cb end
# File ext/openssl/lib/openssl/ssl.rb, line 440 def session_new_cb @context.session_new_cb end
向对端发送“关闭通知”,并尝试优雅地关闭 SSL
连接。
static VALUE ossl_ssl_stop(VALUE self) { SSL *ssl; int ret; GetSSL(self, ssl); if (!ssl_started(ssl)) return Qnil; ret = SSL_shutdown(ssl); if (ret == 1) /* Have already received close_notify */ return Qnil; if (ret == 0) /* Sent close_notify, but we don't wait for reply */ return Qnil; /* * XXX: Something happened. Possibly it failed because the underlying socket * is not writable/readable, since it is in non-blocking mode. We should do * some proper error handling using SSL_get_error() and maybe retry, but we * can't block here. Give up for now. */ ossl_clear_error(); return Qnil; }
# File ext/openssl/lib/openssl/ssl.rb, line 436 def tmp_dh_callback @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK end
# File ext/openssl/lib/openssl/ssl.rb, line 426 def using_anon_cipher? ctx = OpenSSL::SSL::SSLContext.new ctx.ciphers = "aNULL" ctx.ciphers.include?(cipher) end