Spec-Zone.ru › Ruby 3.2

класс OpenSSL::X509::Certificate

Родитель:
Объект
Включенные модули:
OpenSSL::Marshal, OpenSSL::X509::Extension::SubjectKeyIdentifier, OpenSSL::X509::Extension::AuthorityKeyIdentifier, OpenSSL::X509::Extension::CRLDistributionPoints, OpenSSL::X509::Extension::AuthorityInfoAccess

Реализация сертификата X.509, как указано в RFC 5280. Предоставляет доступ к атрибутам сертификата и позволяет считывать сертификаты из строки, но также поддерживает создание новых сертификатов с нуля.

Чтение сертификата из файла

Certificate способен обрабатывать DER-кодированные сертификаты и сертификаты, закодированные в формате PEM OpenSSL.

raw = File.binread "cert.cer" # DER- or PEM-encoded
certificate = OpenSSL::X509::Certificate.new raw

Сохранение сертификата в файл

Сертификат может быть закодирован в формате DER

cert = ...
File.open("cert.cer", "wb") { |f| f.print cert.to_der }

или в формате PEM

cert = ...
File.open("cert.pem", "wb") { |f| f.print cert.to_pem }

Сертификаты X.509 связаны с парой открытый/закрытый ключ, обычно RSA, DSA или ECC (см. также OpenSSL::PKey::RSA, OpenSSL::PKey::DSA и OpenSSL::PKey::EC), сам открытый ключ хранится в сертификате и может быть получен в виде OpenSSL::PKey. Сертификаты обычно используются для связывания какой-либо формы идентификатора с парой ключей, например, веб-серверы, предоставляющие страницы по протоколу HTTPs, используют сертификаты для аутентификации перед пользователем.

Модель инфраструктуры открытых ключей (PKI) полагается на доверенные центры сертификации («корневые ЦС»), которые выдают эти сертификаты, поэтому конечным пользователям необходимо доверять только избранным нескольким авторитетам, которые сами гарантируют доверенность подчиненных ЦС, выдавающие сертификаты конечным пользователям.

Модуль OpenSSL::X509 предоставляет инструменты для настройки независимой PKI, аналогично сценариям, где утилита командной строки «openssl» используется для выдачи сертификатов в частной PKI.

Создание сертификата корневого ЦС и сертификата конечного пользователя

Сначала нам нужно создать «самоподписанный» корневой сертификат. Для этого сначала нужно сгенерировать ключ. Обратите внимание, что выбор «1» в качестве серийного номера считается уязвимостью безопасности для реальных сертификатов. Безопасные варианты – целые числа в диапазоне двух байтов и, желательно, не последовательные, а защищенные случайные числа. Эти шаги опушены для краткости примера.

root_key = OpenSSL::PKey::RSA.new 2048 # the CA's public/private key
root_ca = OpenSSL::X509::Certificate.new
root_ca.version = 2 # cf. RFC 5280 - to make it a "v3" certificate
root_ca.serial = 1
root_ca.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby CA"
root_ca.issuer = root_ca.subject # root CA's are "self-signed"
root_ca.public_key = root_key.public_key
root_ca.not_before = Time.now
root_ca.not_after = root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = root_ca
ef.issuer_certificate = root_ca
root_ca.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true))
root_ca.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
root_ca.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
root_ca.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
root_ca.sign(root_key, OpenSSL::Digest.new('SHA256'))

Следующим шагом является создание сертификата конечного пользователя, используя сертификат корневого ЦС.

key = OpenSSL::PKey::RSA.new 2048
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 2
cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby certificate"
cert.issuer = root_ca.subject # root CA is the issuer
cert.public_key = key.public_key
cert.not_before = Time.now
cert.not_after = cert.not_before + 1 * 365 * 24 * 60 * 60 # 1 years validity
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = root_ca
cert.add_extension(ef.create_extension("keyUsage","digitalSignature", true))
cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
cert.sign(root_key, OpenSSL::Digest.new('SHA256'))

Публичные методы класса

OpenSSL::X509::Certificate.load(строка) → [certs...] Показать исходный код
OpenSSL::X509::Certificate.load(файл) → [certs...]
static VALUE
ossl_x509_load(VALUE klass, VALUE buffer)
{
    BIO *in = ossl_obj2bio(&buffer);

    return rb_ensure(load_chained_certificates, (VALUE)in, load_chained_certificates_ensure, (VALUE)in);
}

Считывает цепочку сертификатов из указанного входного потока. Поддерживает как PEM, так и DER-кодированные сертификаты.

PEM — текстовый формат и поддерживает более одного сертификата.

DER — двоичный формат и поддерживает только один сертификат.

Если файл пустой или содержит только не относящиеся данные, будет поднято исключение OpenSSL::X509::CertificateError.

load_file(путь) Показать исходный код
# File ext/openssl/lib/openssl/x509.rb, line 360
def self.load_file(path)
  load(File.binread(path))
end
new → cert Показать исходный код
new(строка) → cert
static VALUE
ossl_x509_initialize(int argc, VALUE *argv, VALUE self)
{
    BIO *in;
    X509 *x509, *x509_orig = RTYPEDDATA_DATA(self);
    VALUE arg;

    rb_check_frozen(self);
    if (rb_scan_args(argc, argv, "01", &arg) == 0) {
        /* create just empty X509Cert */
        return self;
    }
    arg = ossl_to_der_if_possible(arg);
    in = ossl_obj2bio(&arg);
    x509 = d2i_X509_bio(in, NULL);
    if (!x509) {
        OSSL_BIO_reset(in);
        x509 = PEM_read_bio_X509(in, NULL, NULL, NULL);
    }
    BIO_free(in);
    if (!x509)
        ossl_raise(eX509CertError, "PEM_read_bio_X509");

    RTYPEDDATA_DATA(self) = x509;
    X509_free(x509_orig);

    return self;
}

Общедоступные методы экземпляра

cert1 == cert2 → true | false Показать исходный код
static VALUE
ossl_x509_eq(VALUE self, VALUE other)
{
    X509 *a, *b;

    GetX509(self, a);
    if (!rb_obj_is_kind_of(other, cX509Cert))
        return Qfalse;
    GetX509(other, b);

    return !X509_cmp(a, b) ? Qtrue : Qfalse;
}

Сравнивает два сертификата. Обратите внимание, что сравнение учитывает все поля, а не только имя издателя и серийный номер.

add_extension(extension) → extension Показать исходный код
static VALUE
ossl_x509_add_extension(VALUE self, VALUE extension)
{
    X509 *x509;
    X509_EXTENSION *ext;

    GetX509(self, x509);
    ext = GetX509ExtPtr(extension);
    if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext - FREE it */
        ossl_raise(eX509CertError, NULL);
    }

    return extension;
}
check_private_key(key) → true | false Показать исходный код
static VALUE
ossl_x509_check_private_key(VALUE self, VALUE key)
{
    X509 *x509;
    EVP_PKEY *pkey;

    /* not needed private key, but should be */
    pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
    GetX509(self, x509);
    if (!X509_check_private_key(x509, pkey)) {
        ossl_clear_error();
        return Qfalse;
    }

    return Qtrue;
}

Возвращает true , если ключ является соответствующим закрытым ключом к информации о открытом ключе субъекта, false в противном случае.

extensions → [extension...] Показать исходный код
static VALUE
ossl_x509_get_extensions(VALUE self)
{
    X509 *x509;
    int count, i;
    X509_EXTENSION *ext;
    VALUE ary;

    GetX509(self, x509);
    count = X509_get_ext_count(x509);
    if (count < 0) {
        return rb_ary_new();
    }
    ary = rb_ary_new2(count);
    for (i=0; i<count; i++) {
        ext = X509_get_ext(x509, i); /* NO DUP - don't free! */
        rb_ary_push(ary, ossl_x509ext_new(ext));
    }

    return ary;
}
extensions = [ext...] → [ext...] Показать исходный код
static VALUE
ossl_x509_set_extensions(VALUE self, VALUE ary)
{
    X509 *x509;
    X509_EXTENSION *ext;
    long i;

    Check_Type(ary, T_ARRAY);
    /* All ary's members should be X509Extension */
    for (i=0; i<RARRAY_LEN(ary); i++) {
        OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
    }
    GetX509(self, x509);
    for (i = X509_get_ext_count(x509); i > 0; i--)
        X509_EXTENSION_free(X509_delete_ext(x509, 0));
    for (i=0; i<RARRAY_LEN(ary); i++) {
        ext = GetX509ExtPtr(RARRAY_AREF(ary, i));
        if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext */
            ossl_raise(eX509CertError, "X509_add_ext");
        }
    }

    return ary;
}
initialize_copy(p1) Показать исходный код
static VALUE
ossl_x509_copy(VALUE self, VALUE other)
{
    X509 *a, *b, *x509;

    rb_check_frozen(self);
    if (self == other) return self;

    GetX509(self, a);
    GetX509(other, b);

    x509 = X509_dup(b);
    if (!x509) ossl_raise(eX509CertError, NULL);

    DATA_PTR(self) = x509;
    X509_free(a);

    return self;
}
inspect() Показать исходный код
static VALUE
ossl_x509_inspect(VALUE self)
{
    return rb_sprintf("#<%"PRIsVALUE": subject=%+"PRIsVALUE", "
                      "issuer=%+"PRIsVALUE", serial=%+"PRIsVALUE", "
                      "not_before=%+"PRIsVALUE", not_after=%+"PRIsVALUE">",
                      rb_obj_class(self),
                      ossl_x509_get_subject(self),
                      ossl_x509_get_issuer(self),
                      ossl_x509_get_serial(self),
                      ossl_x509_get_not_before(self),
                      ossl_x509_get_not_after(self));
}
issuer → name Показать исходный код
static VALUE
ossl_x509_get_issuer(VALUE self)
{
    X509 *x509;
    X509_NAME *name;

    GetX509(self, x509);
    if(!(name = X509_get_issuer_name(x509))) { /* NO DUP - don't free! */
        ossl_raise(eX509CertError, NULL);
    }

    return ossl_x509name_new(name);
}
issuer = name → name Показать исходный код
static VALUE
ossl_x509_set_issuer(VALUE self, VALUE issuer)
{
    X509 *x509;

    GetX509(self, x509);
    if (!X509_set_issuer_name(x509, GetX509NamePtr(issuer))) { /* DUPs name */
        ossl_raise(eX509CertError, NULL);
    }

    return issuer;
}
not_after → time Показать исходный код
static VALUE
ossl_x509_get_not_after(VALUE self)
{
    X509 *x509;
    const ASN1_TIME *asn1time;

    GetX509(self, x509);
    if (!(asn1time = X509_get0_notAfter(x509))) {
        ossl_raise(eX509CertError, NULL);
    }

    return asn1time_to_time(asn1time);
}
not_after = time → time Показать исходный код
static VALUE
ossl_x509_set_not_after(VALUE self, VALUE time)
{
    X509 *x509;
    ASN1_TIME *asn1time;

    GetX509(self, x509);
    asn1time = ossl_x509_time_adjust(NULL, time);
    if (!X509_set1_notAfter(x509, asn1time)) {
        ASN1_TIME_free(asn1time);
        ossl_raise(eX509CertError, "X509_set_notAfter");
    }
    ASN1_TIME_free(asn1time);

    return time;
}
not_before → time Показать исходный код
static VALUE
ossl_x509_get_not_before(VALUE self)
{
    X509 *x509;
    const ASN1_TIME *asn1time;

    GetX509(self, x509);
    if (!(asn1time = X509_get0_notBefore(x509))) {
        ossl_raise(eX509CertError, NULL);
    }

    return asn1time_to_time(asn1time);
}
not_before = time → time Показать исходный код
static VALUE
ossl_x509_set_not_before(VALUE self, VALUE time)
{
    X509 *x509;
    ASN1_TIME *asn1time;

    GetX509(self, x509);
    asn1time = ossl_x509_time_adjust(NULL, time);
    if (!X509_set1_notBefore(x509, asn1time)) {
        ASN1_TIME_free(asn1time);
        ossl_raise(eX509CertError, "X509_set_notBefore");
    }
    ASN1_TIME_free(asn1time);

    return time;
}
pretty_print(q) Показать исходный код
# File ext/openssl/lib/openssl/x509.rb, line 349
def pretty_print(q)
  q.object_group(self) {
    q.breakable
    q.text 'subject='; q.pp self.subject; q.text ','; q.breakable
    q.text 'issuer='; q.pp self.issuer; q.text ','; q.breakable
    q.text 'serial='; q.pp self.serial; q.text ','; q.breakable
    q.text 'not_before='; q.pp self.not_before; q.text ','; q.breakable
    q.text 'not_after='; q.pp self.not_after
  }
end
public_key → key Показать исходный код
static VALUE
ossl_x509_get_public_key(VALUE self)
{
    X509 *x509;
    EVP_PKEY *pkey;

    GetX509(self, x509);
    if (!(pkey = X509_get_pubkey(x509))) { /* adds an reference */
        ossl_raise(eX509CertError, NULL);
    }

    return ossl_pkey_new(pkey); /* NO DUP - OK */
}
public_key = key Показать исходный код
static VALUE
ossl_x509_set_public_key(VALUE self, VALUE key)
{
    X509 *x509;
    EVP_PKEY *pkey;

    GetX509(self, x509);
    pkey = GetPKeyPtr(key);
    ossl_pkey_check_public_key(pkey);
    if (!X509_set_pubkey(x509, pkey))
        ossl_raise(eX509CertError, "X509_set_pubkey");
    return key;
}
serial → integer Показать исходный код
static VALUE
ossl_x509_get_serial(VALUE self)
{
    X509 *x509;

    GetX509(self, x509);

    return asn1integer_to_num(X509_get_serialNumber(x509));
}
serial = integer → integer Показать исходный код
static VALUE
ossl_x509_set_serial(VALUE self, VALUE num)
{
    X509 *x509;

    GetX509(self, x509);
    X509_set_serialNumber(x509, num_to_asn1integer(num, X509_get_serialNumber(x509)));

    return num;
}
sign(key, digest) → self Показать исходный код
static VALUE
ossl_x509_sign(VALUE self, VALUE key, VALUE digest)
{
    X509 *x509;
    EVP_PKEY *pkey;
    const EVP_MD *md;

    pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
    md = ossl_evp_get_digestbyname(digest);
    GetX509(self, x509);
    if (!X509_sign(x509, pkey, md)) {
        ossl_raise(eX509CertError, NULL);
    }

    return self;
}
signature_algorithm → string Показать исходный код
static VALUE
ossl_x509_get_signature_algorithm(VALUE self)
{
    X509 *x509;
    BIO *out;
    VALUE str;

    GetX509(self, x509);
    out = BIO_new(BIO_s_mem());
    if (!out) ossl_raise(eX509CertError, NULL);

    if (!i2a_ASN1_OBJECT(out, X509_get0_tbs_sigalg(x509)->algorithm)) {
        BIO_free(out);
        ossl_raise(eX509CertError, NULL);
    }
    str = ossl_membio2str(out);

    return str;
}
subject → name Показать исходный код
static VALUE
ossl_x509_get_subject(VALUE self)
{
    X509 *x509;
    X509_NAME *name;

    GetX509(self, x509);
    if (!(name = X509_get_subject_name(x509))) { /* NO DUP - don't free! */
        ossl_raise(eX509CertError, NULL);
    }

    return ossl_x509name_new(name);
}
subject = name → name Показать исходный код
static VALUE
ossl_x509_set_subject(VALUE self, VALUE subject)
{
    X509 *x509;

    GetX509(self, x509);
    if (!X509_set_subject_name(x509, GetX509NamePtr(subject))) { /* DUPs name */
        ossl_raise(eX509CertError, NULL);
    }

    return subject;
}
to_der → string Показать исходный код
static VALUE
ossl_x509_to_der(VALUE self)
{
    X509 *x509;
    VALUE str;
    long len;
    unsigned char *p;

    GetX509(self, x509);
    if ((len = i2d_X509(x509, NULL)) <= 0)
        ossl_raise(eX509CertError, NULL);
    str = rb_str_new(0, len);
    p = (unsigned char *)RSTRING_PTR(str);
    if (i2d_X509(x509, &p) <= 0)
        ossl_raise(eX509CertError, NULL);
    ossl_str_adjust(str, p);

    return str;
}
to_pem → string Показать исходный код
static VALUE
ossl_x509_to_pem(VALUE self)
{
    X509 *x509;
    BIO *out;
    VALUE str;

    GetX509(self, x509);
    out = BIO_new(BIO_s_mem());
    if (!out) ossl_raise(eX509CertError, NULL);

    if (!PEM_write_bio_X509(out, x509)) {
        BIO_free(out);
        ossl_raise(eX509CertError, NULL);
    }
    str = ossl_membio2str(out);

    return str;
}
Также алиас: to_s
to_s()
Псевдоним для: to_pem
to_text → string Показать исходный код
static VALUE
ossl_x509_to_text(VALUE self)
{
    X509 *x509;
    BIO *out;
    VALUE str;

    GetX509(self, x509);

    out = BIO_new(BIO_s_mem());
    if (!out) ossl_raise(eX509CertError, NULL);

    if (!X509_print(out, x509)) {
        BIO_free(out);
        ossl_raise(eX509CertError, NULL);
    }
    str = ossl_membio2str(out);

    return str;
}
verify(key) → true | false Показать исходный код
static VALUE
ossl_x509_verify(VALUE self, VALUE key)
{
    X509 *x509;
    EVP_PKEY *pkey;

    GetX509(self, x509);
    pkey = GetPKeyPtr(key);
    ossl_pkey_check_public_key(pkey);
    switch (X509_verify(x509, pkey)) {
      case 1:
        return Qtrue;
      case 0:
        ossl_clear_error();
        return Qfalse;
      default:
        ossl_raise(eX509CertError, NULL);
    }
}

Проверяет подпись сертификата с использованием открытого ключа key. key должен быть экземпляром OpenSSL::PKey.

version → integer Показать исходный код
static VALUE
ossl_x509_get_version(VALUE self)
{
    X509 *x509;

    GetX509(self, x509);

    return LONG2NUM(X509_get_version(x509));
}
version = integer → integer Показать исходный код
static VALUE
ossl_x509_set_version(VALUE self, VALUE version)
{
    X509 *x509;
    long ver;

    if ((ver = NUM2LONG(version)) < 0) {
        ossl_raise(eX509CertError, "version must be >= 0!");
    }
    GetX509(self, x509);
    if (!X509_set_version(x509, ver)) {
        ossl_raise(eX509CertError, NULL);
    }

    return version;
}

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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