класс OpenSSL::Timestamp::Factory
Используется для генерации Response с нуля.
Обратите внимание, что реализация всегда будет применять и отдавать предпочтение идентификатору политики, указанному в запросе, над идентификатором политики по умолчанию, указанным в Factory. Вследствие этого, default_policy_id будет применён только в том случае, если не был задан Request#policy_id. Но это также означает, что необходимо вручную проверить идентификатор политики в запросе перед созданием Response, например, чтобы проверить, соответствует ли он определённому набору допустимых политик.
Также существует возможность добавить сертификаты (экземпляры OpenSSL::X509::Certificate) помимо сертификата маркировки времени, которые будут включены в полученный токен отметки времени, если Request#cert_requested? равен true. В идеале следует также включить все промежуточные сертификаты (корневой сертификат можно опустить — для его доверия любой проверяющей стороне всё равно нужно обладать им). Это упрощает проверку отметки времени, так как эти промежуточные сертификаты «уже есть» и не нужно больше передавать их как внешние параметры в Response#verify, тем самым сводя к минимуму необходимые для проверки внешние ресурсы.
Пример: Включение (ненадёжных) промежуточных сертификатов
Предположим, мы получили запрос на отметку времени, в котором Request#policy_id задан как nil и Request#cert_requested? равен true. Необработанные байты запроса хранятся в переменной под названием req_raw. Мы всё ещё хотели бы интегрировать необходимые промежуточные сертификаты (в inter1.cer и inter2.cer для упрощения проверки полученного Response. ts.p12 — это совместимый с PKCS#12 файл, содержащий приватный ключ и сертификат маркировки времени.
req = OpenSSL::Timestamp::Request.new(raw_bytes)
p12 = OpenSSL::PKCS12.new(File.binread('ts.p12'), 'pwd')
inter1 = OpenSSL::X509::Certificate.new(File.binread('inter1.cer'))
inter2 = OpenSSL::X509::Certificate.new(File.binread('inter2.cer'))
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
fac.allowed_digests = ["sha256", "sha384", "sha512"]
#needed because the Request contained no policy identifier
fac.default_policy_id = '1.2.3.4.5'
fac.additional_certificates = [ inter1, inter2 ]
timestamp = fac.create_timestamp(p12.key, p12.certificate, req)
Атрибуты
default_policy_id
Request#policy_id всегда будет отдаваться предпочтение, если он присутствует в Request, только если Request#policy_id имеет значение nil, будет использоваться default_policy. Если ни один из них не присутствует, при попытке создания Response будет возбуждено исключение TimestampError.
вызов:
factory.default_policy_id = "string" -> string factory.default_policy_id -> string or nil
serial_number
Устанавливает или извлекает порядковый номер, который будет использоваться для создания отметки времени. Он должен присутствовать для создания отметки времени.
вызов:
factory.serial_number = number -> number factory.serial_number -> number or nil
gen_time
Устанавливает или извлекает значение Time, которое будет использоваться в Response. Он должен присутствовать для создания отметки времени.
вызов:
factory.gen_time = Time -> Time factory.gen_time -> Time or nil
additional_certs
Устанавливает или извлекает дополнительные сертификаты помимо сертификата маркировки времени (например, промежуточные сертификаты), которые будут добавлены в Response. Должен быть Array из OpenSSL::X509::Certificate.
вызов:
factory.additional_certs = [cert1, cert2] -> [ cert1, cert2 ] factory.additional_certs -> array or nil
allowed_digests
Устанавливает или извлекает алгоритмы хеширования, которые фабрика разрешает использовать для создания отметки времени. Известные уязвимые или слабые алгоритмы должны быть запрещены, где это возможно. Должен быть Array из String или экземпляров подклассов OpenSSL::Digest.
вызов:
factory.allowed_digests = ["sha1", OpenSSL::Digest.new('SHA256').new] -> [ "sha1", OpenSSL::Digest) ]
factory.allowed_digests -> array or nil Атрибуты
Открытые методы экземпляра
static VALUE
ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request)
{
VALUE serial_number, def_policy_id, gen_time, additional_certs, allowed_digests;
VALUE str;
STACK_OF(X509) *inter_certs;
VALUE tsresp, ret = Qnil;
EVP_PKEY *sign_key;
X509 *tsa_cert;
TS_REQ *req;
TS_RESP *response = NULL;
TS_RESP_CTX *ctx = NULL;
BIO *req_bio;
ASN1_INTEGER *asn1_serial = NULL;
ASN1_OBJECT *def_policy_id_obj = NULL;
long lgen_time;
const char * err_msg = NULL;
int status = 0;
tsresp = NewTSResponse(cTimestampResponse);
tsa_cert = GetX509CertPtr(certificate);
sign_key = GetPrivPKeyPtr(key);
GetTSRequest(request, req);
gen_time = ossl_tsfac_get_gen_time(self);
if (!rb_obj_is_instance_of(gen_time, rb_cTime)) {
err_msg = "@gen_time must be a Time.";
goto end;
}
lgen_time = NUM2LONG(rb_funcall(gen_time, rb_intern("to_i"), 0));
serial_number = ossl_tsfac_get_serial_number(self);
if (NIL_P(serial_number)) {
err_msg = "@serial_number must be set.";
goto end;
}
asn1_serial = num_to_asn1integer(serial_number, NULL);
def_policy_id = ossl_tsfac_get_default_policy_id(self);
if (NIL_P(def_policy_id) && !TS_REQ_get_policy_id(req)) {
err_msg = "No policy id in the request and no default policy set";
goto end;
}
if (!NIL_P(def_policy_id) && !TS_REQ_get_policy_id(req)) {
def_policy_id_obj = (ASN1_OBJECT*)rb_protect(obj_to_asn1obj_i, (VALUE)def_policy_id, &status);
if (status)
goto end;
}
if (!(ctx = TS_RESP_CTX_new())) {
err_msg = "Memory allocation failed.";
goto end;
}
TS_RESP_CTX_set_serial_cb(ctx, ossl_tsfac_serial_cb, &asn1_serial);
if (!TS_RESP_CTX_set_signer_cert(ctx, tsa_cert)) {
err_msg = "Certificate does not contain the timestamping extension";
goto end;
}
additional_certs = ossl_tsfac_get_additional_certs(self);
if (rb_obj_is_kind_of(additional_certs, rb_cArray)) {
inter_certs = ossl_protect_x509_ary2sk(additional_certs, &status);
if (status)
goto end;
/* this dups the sk_X509 and ups each cert's ref count */
TS_RESP_CTX_set_certs(ctx, inter_certs);
sk_X509_pop_free(inter_certs, X509_free);
}
TS_RESP_CTX_set_signer_key(ctx, sign_key);
if (!NIL_P(def_policy_id) && !TS_REQ_get_policy_id(req))
TS_RESP_CTX_set_def_policy(ctx, def_policy_id_obj);
if (TS_REQ_get_policy_id(req))
TS_RESP_CTX_set_def_policy(ctx, TS_REQ_get_policy_id(req));
TS_RESP_CTX_set_time_cb(ctx, ossl_tsfac_time_cb, &lgen_time);
allowed_digests = ossl_tsfac_get_allowed_digests(self);
if (rb_obj_is_kind_of(allowed_digests, rb_cArray)) {
int i;
VALUE rbmd;
const EVP_MD *md;
for (i = 0; i < RARRAY_LEN(allowed_digests); i++) {
rbmd = rb_ary_entry(allowed_digests, i);
md = (const EVP_MD *)rb_protect(ossl_evp_get_digestbyname_i, rbmd, &status);
if (status)
goto end;
TS_RESP_CTX_add_md(ctx, md);
}
}
str = rb_protect(ossl_to_der, request, &status);
if (status)
goto end;
req_bio = (BIO*)rb_protect(ossl_obj2bio_i, (VALUE)&str, &status);
if (status)
goto end;
response = TS_RESP_create_response(ctx, req_bio);
BIO_free(req_bio);
if (!response) {
err_msg = "Error during response generation";
goto end;
}
/* bad responses aren't exceptional, but openssl still sets error
* information. */
ossl_clear_error();
SetTSResponse(tsresp, response);
ret = tsresp;
end:
ASN1_INTEGER_free(asn1_serial);
ASN1_OBJECT_free(def_policy_id_obj);
TS_RESP_CTX_free(ctx);
if (err_msg)
rb_exc_raise(ossl_make_error(eTimestampError, rb_str_new_cstr(err_msg)));
if (status)
rb_jump_tag(status);
return ret;
} Создаёт Response с помощью OpenSSL::PKey, OpenSSL::X509::Certificate и Request.
Обязательные параметры для создания отметки времени, которые необходимо установить в запросе:
Обязательные параметры, которые необходимо установить в фабрике:
Кроме того, необходимо установить либо Request#policy_id, либо Factory#default_policy_id.
Возбуждает TimestampError, если создание завершилось неудачно, хотя могут быть возвращены успешно созданные ответы об ошибках.
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.