class 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 для создания метки времени:
Обязательные параметры, которые необходимо задать в Factory:
Кроме того, должен быть задан один из параметров: Request#policy_id или Factory#default_policy_id.
Если создание не удалось, вызывается исключение TimestampError, однако при этом могут возвращаться успешно созданные ответы с ошибкой.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.