Spec-Zone.ru › Ruby 3.4
  1. OpenSSL::
  2. Timestamp::
  3. Factory

class OpenSSL::Timestamp::Factory

Родительский класс:
Object

Используется для создания 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

Атрибуты

additional_certs [RW]
allowed_digests [RW]
default_policy_id [RW]
gen_time [RW]
serial_number [RW]

Открытые методы экземпляра

create_timestamp(key, certificate, request) → Response Показать исходный код
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 для создания метки времени:

  • Request#algorithm

  • Request#message_imprint

Обязательные параметры, которые необходимо задать в Factory:

  • Factory#serial_number

  • Factory#gen_time

  • Factory#allowed_digests

Кроме того, должен быть задан один из параметров: 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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API