class OpenSSL::SSL::SSLContext
Класс SSLContext используется для настройки различных параметров сертификатов, алгоритмов, проверки, кэширования сессий и т. д. Класс SSLContext используется для создания объекта SSLSocket.
Все атрибуты должны быть установлены до создания объекта SSLSocket, так как SSLContext будет заморожен после этого.
Следующие атрибуты доступны, но не отображаются в rdoc:
-
ssl_version, cert, key, #client_ca, #ca_file, #ca_path, timeout,
-
#verify_mode, #verify_depth #client_cert_cb, #tmp_dh_callback,
-
#session_id_context, session_add_cb, #session_new_cb, #session_remove_cb
Константы
- DEFAULT_CERT_STORE
- DEFAULT_PARAMS
- METHODS
-
Список доступных методов SSL/TLS
- 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
-
Серверные сессии добавляются в кэш сессий
Атрибуты
Путь к файлу, содержащему сертификат CA в формате PEM.
Путь к каталогу, содержащему сертификаты CA в формате PEM.
Файлы ищутся по значению хэша имени субъекта X509.
Сертификат контекста.
Объект OpenSSL::X509::Store, используемый для проверки сертификатов.
Сертификат или массив сертификатов, которые будут отправлены клиенту.
Обратный вызов, вызываемый, когда сервер запрашивает сертификат клиента, и сертификат не был установлен.
Обратный вызов вызывается с Session и должен возвращать массив, содержащий OpenSSL::X509::Certificate и OpenSSL::PKey. Возврат любого другого значения приостановит рукопожатие.
Массив дополнительных сертификатов X509, которые нужно добавить в цепочку сертификатов.
Приватный ключ контекста.
Перечисляемый объект строк. Каждая строка представляет протокол, который будет объявлен как список поддерживаемых протоколов для Next Protocol Negotiation. Поддерживается в OpenSSL 1.0.1 и выше. Не имеет эффекта на стороне клиента. Если не указано явно, расширение NPN не будет отправлено сервером в процессе рукопожатия.
Пример
ctx.npn_protocols = ["http/1.1", "spdy/2"]
Обратный вызов, вызываемый на стороне клиента, когда клиенту нужно выбрать протокол из списка, отправленного сервером. Поддерживается в OpenSSL 1.0.1 и выше. Клиент ДОЛЖЕН выбрать протокол из тех, которые объявлены сервером. Если ни один не приемлем, сгенерирование ошибки в обратном вызове приведёт к сбою рукопожатия. Если этот обратный вызов не задан явно, поддержка расширения NPN на стороне клиента отключена — любые объявленные сервером протоколы будут проигнорированы.
Пример
ctx.npn_select_cb = lambda do |protocols| #inspect the protocols and select one protocols.first end
Устанавливает различные параметры OpenSSL.
Обратный вызов, вызываемый всякий раз, когда инициируется новое рукопожатие. Может использоваться для полного отключения повторного согласования.
Обратный вызов вызывается с активным SSLSocket. Значение возврата обратного вызова не имеет значения; нормальное возвращение означает «утверждение» повторного согласования и продолжение процесса. Чтобы запретить повторное согласование и отменить процесс, в обратном вызове может быть сгенерирована ошибка.
Отключение повторного согласования клиента
При работе с сервером часто желательно полностью отключить повторное согласование клиента. Для реализации этой функции можно использовать обратный вызов следующим образом:
num_handshakes = 0
ctx.renegotiation_cb = lambda do |ssl|
num_handshakes += 1
raise RuntimeError.new("Client renegotiation disabled") if num_handshakes > 1
end
Обратный вызов, вызываемый во время подключения для различения нескольких имён серверов.
Обратный вызов вызывается с SSLSocket и именем сервера. Обратный вызов должен вернуть SSLContext для имени сервера или nil.
Устанавливает контекст, в котором сеанс может быть повторно использован. Это позволяет различать сеансы для нескольких приложений, например, по имени.
Обратный вызов, вызываемый, когда был согласован новый сеанс.
Обратный вызов вызывается с SSLSocket. Если возвращается false, сеанс будет удалён из внутренней кэша.
Обратный вызов, вызываемый при удалении сеанса из внутренней кэша.
Обратный вызов вызывается с SSLContext и Session.
Максимальная продолжительность жизни сеанса.
Максимальная продолжительность жизни сеанса.
Обратный вызов, вызываемый при необходимости параметров DH.
Обратный вызов вызывается с Session для обмена ключами, флагом, указывающим на использование шифра экспорта, и необходимой длиной ключа.
Обратный вызов должен вернуть экземпляр OpenSSL::PKey::DH правильной длины ключа.
Обратный вызов для дополнительной проверки сертификатов. Обратный вызов вызывается для каждого сертификата в цепочке.
Обратный вызов вызывается со значениями. preverify_ok указывает, пройдена ли проверка (true) или нет (false). store_context — это OpenSSL::X509::StoreContext, содержащий контекст, используемый для проверки сертификатов.
Если обратный вызов возвращает false, проверка останавливается.
Количество сертификатов CA, которые нужно пройти при проверке цепочки сертификатов.
Режим проверки сеанса Session.
Допустимые режимы — VERIFY_NONE, VERIFY_PEER, VERIFY_CLIENT_ONCE, VERIFY_FAIL_IF_NO_PEER_CERT и определены в OpenSSL::SSL.
Общедоступные методы класса
static VALUE
ossl_sslctx_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE ssl_method;
int i;
for(i = 0; i < numberof(ossl_sslctx_attrs); i++){
char buf[32];
snprintf(buf, sizeof(buf), "@%s", ossl_sslctx_attrs[i]);
rb_iv_set(self, buf, Qnil);
}
if (rb_scan_args(argc, argv, "01", &ssl_method) == 0){
return self;
}
ossl_sslctx_set_ssl_version(self, ssl_method);
return self;
} Вы можете получить список допустимых методов с помощью OpenSSL::SSL::SSLContext::METHODS
Методы публичного экземпляра
static VALUE
ossl_sslctx_get_ciphers(VALUE self)
{
SSL_CTX *ctx;
STACK_OF(SSL_CIPHER) *ciphers;
SSL_CIPHER *cipher;
VALUE ary;
int i, num;
GetSSLCTX(self, ctx);
if(!ctx){
rb_warning("SSL_CTX is not initialized.");
return Qnil;
}
ciphers = ctx->cipher_list;
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;
} Список шифров, настроенных для данного контекста.
static VALUE
ossl_sslctx_set_ciphers(VALUE self, VALUE v)
{
SSL_CTX *ctx;
VALUE str, elem;
int i;
rb_check_frozen(self);
if (NIL_P(v))
return v;
else if (RB_TYPE_P(v, T_ARRAY)) {
str = rb_str_new(0, 0);
for (i = 0; i < RARRAY_LEN(v); i++) {
elem = rb_ary_entry(v, i);
if (RB_TYPE_P(elem, T_ARRAY)) elem = rb_ary_entry(elem, 0);
elem = rb_String(elem);
rb_str_append(str, elem);
if (i < RARRAY_LEN(v)-1) rb_str_cat2(str, ":");
}
} else {
str = v;
StringValue(str);
}
GetSSLCTX(self, ctx);
if(!ctx){
ossl_raise(eSSLError, "SSL_CTX is not initialized.");
return Qnil;
}
if (!SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(str))) {
ossl_raise(eSSLError, "SSL_CTX_set_cipher_list");
}
return v;
} Устанавливает список доступных шифров для данного контекста. В серверном контексте некоторые шифры требуют соответствующих сертификатов. Например, шифр RSA может быть выбран только при наличии сертификата RSA.
См. также OpenSSL::Cipher и OpenSSL::Cipher.ciphers
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;
} Удаляет сессии в кэше, которые истекли в time.
static VALUE
ossl_sslctx_session_add(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
SSL_SESSION *sess;
GetSSLCTX(self, ctx);
SafeGetSSLSession(arg, sess);
return SSL_CTX_add_session(ctx, sess) == 1 ? Qtrue : Qfalse;
} Добавляет session в кэш сессий.
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));
} Текущий режим кэширования сессий.
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;
} Устанавливает режим кэширования сессий SSL. Объедините нужные константы SESSION_CACHE_* для установки. Подробности см. в SSL_CTX_set_session_cache_mode(3).
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;
} Устанавливает размер кэша сессий. Возвращает предыдущий допустимый размер кэша сессий. Ноль используется для обозначения неограниченного размера кэша сессий.
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;
} Возвращает 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_session_remove(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
SSL_SESSION *sess;
GetSSLCTX(self, ctx);
SafeGetSSLSession(arg, sess);
return SSL_CTX_remove_session(ctx, sess) == 1 ? Qtrue : Qfalse;
} Удаляет session из кэша сессий
# File ext/openssl/lib/openssl/ssl.rb, line 85
def set_params(params={})
params = DEFAULT_PARAMS.merge(params)
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 Устанавливает параметры для этого контекста SSL со значениями в params. Ключи в params должны быть методами присваивания в SSLContext.
Если #verify_mode не равен VERIFY_NONE и #ca_file, #ca_path и #cert_store не установлены, используется системный по умолчанию магазин сертификатов.
static VALUE
ossl_sslctx_setup(VALUE self)
{
SSL_CTX *ctx;
X509 *cert = NULL, *client_ca = NULL;
X509_STORE *store;
EVP_PKEY *key = NULL;
char *ca_path = NULL, *ca_file = NULL;
int i, verify_mode;
VALUE val;
if(OBJ_FROZEN(self)) return Qnil;
GetSSLCTX(self, ctx);
#if !defined(OPENSSL_NO_DH)
if (RTEST(ossl_sslctx_get_tmp_dh_cb(self))){
SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback);
}
else{
SSL_CTX_set_tmp_dh_callback(ctx, ossl_default_tmp_dh_callback);
}
#endif
SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ptr_idx, (void*)self);
val = ossl_sslctx_get_cert_store(self);
if(!NIL_P(val)){
/*
* WORKAROUND:
* X509_STORE can count references, but
* X509_STORE_free() doesn't care it.
* So we won't increment it but mark it by ex_data.
*/
store = GetX509StorePtr(val); /* NO NEED TO DUP */
SSL_CTX_set_cert_store(ctx, store);
SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_store_p, (void*)1);
}
val = ossl_sslctx_get_extra_cert(self);
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 = ossl_sslctx_get_cert(self);
cert = NIL_P(val) ? NULL : GetX509CertPtr(val); /* NO DUP NEEDED */
val = ossl_sslctx_get_key(self);
key = NIL_P(val) ? NULL : GetPKeyPtr(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 = ossl_sslctx_get_client_ca(self);
if(!NIL_P(val)){
if (RB_TYPE_P(val, T_ARRAY)) {
for(i = 0; i < RARRAY_LEN(val); i++){
client_ca = GetX509CertPtr(RARRAY_PTR(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 = ossl_sslctx_get_ca_file(self);
ca_file = NIL_P(val) ? NULL : StringValuePtr(val);
val = ossl_sslctx_get_ca_path(self);
ca_path = NIL_P(val) ? NULL : StringValuePtr(val);
if(ca_file || ca_path){
if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path))
rb_warning("can't set verify locations");
}
val = ossl_sslctx_get_verify_mode(self);
verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val);
SSL_CTX_set_verify(ctx, verify_mode, ossl_ssl_verify_callback);
if (RTEST(ossl_sslctx_get_client_cert_cb(self)))
SSL_CTX_set_client_cert_cb(ctx, ossl_client_cert_cb);
val = ossl_sslctx_get_timeout(self);
if(!NIL_P(val)) SSL_CTX_set_timeout(ctx, NUM2LONG(val));
val = ossl_sslctx_get_verify_dep(self);
if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val));
val = ossl_sslctx_get_options(self);
if(!NIL_P(val)) {
SSL_CTX_set_options(ctx, NUM2LONG(val));
} else {
SSL_CTX_set_options(ctx, SSL_OP_ALL);
}
#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
val = rb_iv_get(self, "@npn_protocols");
if (!NIL_P(val)) {
ssl_npn_encode_protocols(self, val);
SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *) self);
OSSL_Debug("SSL NPN advertise callback added");
}
if (RTEST(rb_iv_get(self, "@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
rb_obj_freeze(self);
val = ossl_sslctx_get_sess_id_ctx(self);
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_iv_get(self, "@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_iv_get(self, "@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_iv_get(self, "@session_remove_cb"))) {
SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb);
OSSL_Debug("SSL SESSION remove callback added");
}
#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
val = rb_iv_get(self, "@servername_cb");
if (!NIL_P(val)) {
SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
OSSL_Debug("SSL TLSEXT servername callback added");
}
#endif
return Qtrue;
} Этот метод вызывается автоматически при создании нового SSLSocket. Однако он не потокобезопасен и должен вызываться перед созданием объектов SSLSocket в многопоточном приложении.
static VALUE
ossl_sslctx_set_ssl_version(VALUE self, VALUE ssl_method)
{
SSL_METHOD *method = NULL;
const char *s;
int i;
SSL_CTX *ctx;
if (RB_TYPE_P(ssl_method, T_SYMBOL))
s = rb_id2name(SYM2ID(ssl_method));
else
s = StringValuePtr(ssl_method);
for (i = 0; i < numberof(ossl_ssl_method_tab); i++) {
if (strcmp(ossl_ssl_method_tab[i].name, s) == 0) {
method = ossl_ssl_method_tab[i].func();
break;
}
}
if (!method) {
ossl_raise(rb_eArgError, "unknown SSL method `%s'.", s);
}
GetSSLCTX(self, ctx);
if (SSL_CTX_set_ssl_version(ctx, method) != 1) {
ossl_raise(eSSLError, "SSL_CTX_set_ssl_version");
}
return ssl_method;
} Список допустимых версий можно получить с помощью OpenSSL::SSL::SSLContext::METHODS
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.