类 OpenSSL::X509::Certificate
根据 RFC 5280 规范实现 X.509 证书。提供对证书属性的访问,并允许从字符串读取证书,但也支持从头开始创建新证书。
从文件读取证书¶ ↑
Certificate
能够处理 DER 编码的证书和 OpenSSL 的 PEM 格式编码的证书。
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 提供页面的 Web 服务器使用证书向用户进行身份验证。
公钥基础设施 (PKI) 模型依赖于受信任的证书颁发机构(“根 CA”)来颁发这些证书,因此最终用户只需要信任少数几个机构,而这些机构本身又会为颁发证书给最终用户的下级 CA 提供担保。
The OpenSSL::X509
module provides the tools to set up an independent PKI, similar to scenarios where the ‘openssl’ command line tool is used for issuing certificates in a private PKI.
Creating a root CA certificate and an end-entity certificate¶ ↑
First, we need to create a “self-signed” root certificate. To do so, we need to generate a key first. Please note that the choice of “1” as a serial number is considered a security flaw for real certificates. Secure choices are integers in the two-digit byte range and ideally not sequential but secure random numbers, steps omitted here to keep the example concise.
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'))
The next step is to create the end-entity certificate using the root CA certificate.
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'))
公共类方法
从给定的输入中读取链式证书。支持 PEM 和 DER 编码的证书。
PEM 是一种文本格式,支持多个证书。
DER 是一种二进制格式,只支持一个证书。
如果文件为空,或者只包含无关数据,则会抛出 OpenSSL::X509::CertificateError
异常。
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); }
# File openssl/lib/openssl/x509.rb, line 360 def self.load_file(path) load(File.binread(path)) end
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; }
公共实例方法
比较两个证书。请注意,这会考虑所有字段,而不仅仅是颁发者名称和序列号。
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; }
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; }
如果 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; }
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; }
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; }
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; }
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)); }
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); }
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; }
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); }
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; }
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); }
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; }
# File 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
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 */ }
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; }
static VALUE ossl_x509_get_serial(VALUE self) { X509 *x509; GetX509(self, x509); return asn1integer_to_num(X509_get_serialNumber(x509)); }
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; }
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; }
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; }
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); }
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; }
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; }
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; }
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; }
使用公钥 key 验证证书的签名。key 必须是 OpenSSL::PKey
的实例。
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); } }
static VALUE ossl_x509_get_version(VALUE self) { X509 *x509; GetX509(self, x509); return LONG2NUM(X509_get_version(x509)); }
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; }