class OpenSSL::SSL::SSLSocket
属性
此连接中使用的 SSLContext 对象。
当 SSL/TLS 连接关闭时,是否也关闭底层套接字。默认值为 false。
公共类方法
源代码
static VALUE
ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE io, v_ctx;
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);
rb_call_super(0, NULL);
return self;
}
从 io 创建一个新的 SSL 套接字,io 必须是一个真实的 IO 对象(而不是一个响应 read/write 的类 IO 对象)。
如果提供了 ctx,SSL 套接字的初始参数将从上下文中获取。
OpenSSL::Buffering 模块提供了额外的 IO 方法。
如果提供了 SSLContext,此方法将冻结它;但是,在冻结的 SSLContext 中仍然允许会话管理。
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 533 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
创建一个新的 SSLSocket 实例。remote_host 和 remote_port 用于打开 TCPSocket。如果指定了 local_host 和 local_port,则这些参数用于本地端建立连接。如果提供了 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
公共实例方法
源代码
static VALUE
ossl_ssl_accept(VALUE self)
{
ossl_ssl_setup(self);
return ossl_start_ssl(self, SSL_accept, "SSL_accept", Qfalse);
}
等待 SSL/TLS 客户端发起握手。
源代码
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);
}
以非阻塞方式启动作为服务器的 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_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);
}
返回在握手期间服务器最终选择的 ALPN 协议字符串。
源代码
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);
}
此套接字端点的 X509 证书。
源代码
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;
}
返回当前会话中实际使用的密码套件,如果未建立会话则返回 nil。
源代码
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);
}
返回客户端 CA 的列表。请注意,与 SSLContext#client_ca= 不同,不会返回 X509::Certificate 数组,而是返回 CA 主题可分辨名称的 X509::Name 实例。
在服务器模式下,返回由 SSLContext#client_ca= 设置的列表。在客户端模式下,返回从服务器发送的客户端 CA 列表。
源代码
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 483 def close_write stop end
关闭流以进行写入。此方法的行为取决于 OpenSSL 的版本和使用的 TLS 协议。
-
向对等方发送“close_notify”警报。
-
不等待对等方的“close_notify”警报响应。
在 TLS 1.2 及更早版本中
-
收到“close_notify”警报后,会使用自己的“close_notify”警报进行响应,并立即关闭连接,丢弃任何待处理的写入。
因此,在 TLS 1.2 上,此方法将导致连接完全关闭。在 TLS 1.3 上,连接将保持打开状态,仅用于读取。
源代码
static VALUE
ossl_ssl_connect(VALUE self)
{
ossl_ssl_setup(self);
return ossl_start_ssl(self, SSL_connect, "SSL_connect", Qfalse);
}
启动与服务器的 SSL/TLS 握手。
源代码
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);
}
以非阻塞方式启动作为客户端的 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_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;
}
允许根据 RFC 5705 使用共享会话密钥材料。
源代码
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);
}
返回最后发送的 Finished 消息
源代码
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;
}
设置用于 SNI 的服务器主机名。这需要在 SSLSocket#connect 之前设置。
源代码
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);
}
返回在握手期间客户端最终选择的协议字符串。
源代码
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;
}
此套接字对等方的 X509 证书链。
源代码
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);
}
返回最后接收的 Finished 消息
源代码
static VALUE
ossl_ssl_pending(VALUE self)
{
SSL *ssl;
GetSSL(self, ssl);
return INT2NUM(SSL_pending(ssl));
}
可立即用于读取的字节数。
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 434 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
根据 RFC 6125 执行主机名验证。
此方法必须在调用 connect 之后调用,以确保已验证远程对等方的主机名。
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 455 def session SSL::Session.new(self) rescue SSL::Session::SessionError nil end
返回当前使用的 SSLSession 对象,如果未建立会话则返回 nil。
源代码
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;
}
设置在建立连接时要使用的 Session。
源代码
static VALUE
ossl_ssl_session_reused(VALUE self)
{
SSL *ssl;
GetSSL(self, ssl);
return SSL_session_reused(ssl) ? Qtrue : Qfalse;
}
如果在握手期间协商了重用的会话,则返回 true。
源代码
static VALUE
ossl_ssl_get_version(VALUE self)
{
SSL *ssl;
GetSSL(self, ssl);
return rb_str_new2(SSL_get_version(ssl));
}
返回表示为连接协商的 SSL/TLS 版本的 String,例如 “TLSv1.2”。
源代码
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;
}
当前连接状态的描述。这仅用于诊断目的。
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 421 def sysclose return if closed? stop io.close if sync_close end
向对等方发送 “close notify” 并尝试优雅地关闭 SSL 连接。
如果 sync_close 设置为 true,则也会关闭底层的 IO。
源代码
static VALUE
ossl_ssl_read(int argc, VALUE *argv, VALUE self)
{
return ossl_ssl_read_internal(argc, argv, self, 0);
}
从 SSL 连接读取 length 个字节。如果提供了预先分配的 buffer,则数据将写入其中。
源代码
static VALUE
ossl_ssl_write(VALUE self, VALUE str)
{
return ossl_ssl_write_internal(self, str, Qfalse);
}
将 string 写入 SSL 连接。
源代码
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);
}
返回在使用前向保密密码的情况下使用的临时密钥。
源代码
static VALUE
ossl_ssl_get_verify_result(VALUE self)
{
SSL *ssl;
GetSSL(self, ssl);
return LONG2NUM(SSL_get_verify_result(ssl));
}
返回对等证书验证的结果。有关错误值和说明,请参阅 verify(1)。
如果未提供对等证书,则返回 X509_V_OK。
私有实例方法
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 495 def client_cert_cb @context.client_cert_cb end
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 507 def session_get_cb @context.session_get_cb end
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 503 def session_new_cb @context.session_new_cb end
源代码
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;
}
向对等方发送 “close notify” 并尝试优雅地关闭 SSL 连接。
源代码
源代码
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 499 def tmp_dh_callback @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK end
源代码
# File ext/openssl/lib/openssl/ssl.rb, line 489 def using_anon_cipher? ctx = OpenSSL::SSL::SSLContext.new ctx.ciphers = "aNULL" ctx.ciphers.include?(cipher) end