类 OpenSSL::Timestamp::Factory

用于从头开始生成 Response

请注意,实现将始终应用并优先考虑请求中给出的策略对象标识符,而不是 Factory 中指定的默认策略 ID。因此,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.cerinter2.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

设置或检索将在 Response 中使用的 Time 值。时间戳创建必须存在。

调用序列

factory.gen_time = Time -> Time
factory.gen_time        -> Time or nil

additional_certs

设置或检索除时间戳证书之外的附加证书(例如中间证书),以添加到 Response 中。必须是 OpenSSL::X509::Certificate 的数组。

调用序列

factory.additional_certs = [cert1, cert2] -> [ cert1, cert2 ]
factory.additional_certs                  -> array or nil

allowed_digests

设置或检索工厂允许为其创建时间戳的摘要算法。尽可能不应允许已知的易受攻击或弱算法。必须是字符串或 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 click to toggle source

使用 OpenSSL::PKeyOpenSSL::X509::CertificateRequest 创建 Response

需要在 Request 中设置的时间戳创建的强制参数

需要在 Factory 中设置的强制参数

此外,必须设置 Request#policy_idFactory#default_policy_id 之一。

如果创建失败,则会引发 TimestampError,尽管可能会返回成功创建的错误响应。

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;
}