Class: OpenSSL::X509::Certificate
- Inherits:
-
Object
- Object
- OpenSSL::X509::Certificate
- Includes:
- Marshal, Extension::AuthorityInfoAccess, Extension::AuthorityKeyIdentifier, Extension::CRLDistributionPoints, Extension::SubjectKeyIdentifier
- Defined in:
- ext/openssl/ossl_x509cert.c,
lib/openssl/x509.rb,
ext/openssl/ossl_x509cert.c
Overview
Implementation of an X.509 certificate as specified in RFC 5280. Provides access to a certificate’s attributes and allows certificates to be read from a string, but also supports the creation of new certificates from scratch.
Reading a certificate from a file
Certificate is capable of handling DER-encoded certificates and certificates encoded in OpenSSL’s PEM format.
raw = File.binread "cert.cer" # DER- or PEM-encoded
certificate = OpenSSL::X509::Certificate.new raw
Saving a certificate to a file
A certificate may be encoded in DER format
cert = ...
File.open("cert.cer", "wb") { |f| f.print cert.to_der }
or in PEM format
cert = ...
File.open("cert.pem", "wb") { |f| f.print cert.to_pem }
X.509 certificates are associated with a private/public key pair, typically a RSA, DSA or ECC key (see also OpenSSL::PKey::RSA, OpenSSL::PKey::DSA and OpenSSL::PKey::EC), the public key itself is stored within the certificate and can be accessed in form of an OpenSSL::PKey. Certificates are typically used to be able to associate some form of identity with a key pair, for example web servers serving pages over HTTPs use certificates to authenticate themselves to the user.
The public key infrastructure (PKI) model relies on trusted certificate authorities (“root CAs”) that issue these certificates, so that end users need to base their trust just on a selected few authorities that themselves again vouch for subordinate CAs issuing their certificates to end users.
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'))
Class Method Summary collapse
-
.load(buffer) ⇒ Object
Read the chained certificates from the given input.
- .load_file(path) ⇒ Object
Instance Method Summary collapse
-
#==(cert2) ⇒ Object
Compares the two certificates.
- #add_extension(extension) ⇒ Object
-
#check_private_key(key) ⇒ Object
Returns
true
if key is the corresponding private key to the Subject Public Key Information,false
otherwise. - #extensions ⇒ Array
- #extensions=(ary) ⇒ Object
- #initialize(*args) ⇒ Object constructor
- #initialize_copy(other) ⇒ Object
- #inspect ⇒ Object
- #issuer ⇒ Object
- #issuer=(name) ⇒ Object
- #not_after ⇒ Time
- #not_after=(time) ⇒ Time
- #not_before ⇒ Time
- #not_before=(time) ⇒ Time
- #pretty_print(q) ⇒ Object
- #public_key ⇒ Object
- #public_key=(key) ⇒ Object
- #serial ⇒ Integer
- #serial=(integer) ⇒ Integer
- #sign(key, digest) ⇒ self
- #signature_algorithm ⇒ String
- #subject ⇒ Object
- #subject=(name) ⇒ Object
- #to_der ⇒ String
- #to_pem ⇒ String (also: #to_s)
- #to_text ⇒ String
-
#verify(key) ⇒ Object
Verifies the signature of the certificate, with the public key key.
- #version ⇒ Integer
- #version=(integer) ⇒ Integer
Methods included from Extension::AuthorityInfoAccess
Methods included from Extension::Helpers
Methods included from Extension::CRLDistributionPoints
Methods included from Extension::AuthorityKeyIdentifier
Methods included from Extension::SubjectKeyIdentifier
Methods included from Marshal
Constructor Details
#new ⇒ Object #new(string) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'ext/openssl/ossl_x509cert.c', line 114
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;
}
|
Class Method Details
.OpenSSL::X509::Certificate.load(string) ⇒ Array .OpenSSL::X509::Certificate.load(file) ⇒ Array
Read the chained certificates from the given input. Supports both PEM and DER encoded certificates.
PEM is a text format and supports more than one certificate.
DER is a binary format and only supports one certificate.
If the file is empty, or contains only unrelated data, an OpenSSL::X509::CertificateError
exception will be raised.
853 854 855 856 857 858 859 |
# File 'ext/openssl/ossl_x509cert.c', line 853
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);
}
|
.load_file(path) ⇒ Object
360 361 362 |
# File 'lib/openssl/x509.rb', line 360 def self.load_file(path) load(File.binread(path)) end |
Instance Method Details
#==(cert2) ⇒ Object
Compares the two certificates. Note that this takes into account all fields, not just the issuer name and the serial number.
697 698 699 700 701 702 703 704 705 706 707 708 |
# File 'ext/openssl/ossl_x509cert.c', line 697
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) ⇒ Object
661 662 663 664 665 666 667 668 669 670 671 672 673 674 |
# File 'ext/openssl/ossl_x509cert.c', line 661
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) ⇒ Object
Returns true
if key is the corresponding private key to the Subject Public Key Information, false
otherwise.
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
# File 'ext/openssl/ossl_x509cert.c', line 585
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;
}
|
#extensions ⇒ Array
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 |
# File 'ext/openssl/ossl_x509cert.c', line 606
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=(ary) ⇒ Object
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 |
# File 'ext/openssl/ossl_x509cert.c', line 632
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(other) ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'ext/openssl/ossl_x509cert.c', line 143
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 ⇒ Object
676 677 678 679 680 681 682 683 684 685 686 687 688 |
# File 'ext/openssl/ossl_x509cert.c', line 676
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 ⇒ Object
385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'ext/openssl/ossl_x509cert.c', line 385
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) ⇒ Object
403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'ext/openssl/ossl_x509cert.c', line 403
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
459 460 461 462 463 464 465 466 467 468 469 470 471 |
# File 'ext/openssl/ossl_x509cert.c', line 459
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
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
# File 'ext/openssl/ossl_x509cert.c', line 477
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
420 421 422 423 424 425 426 427 428 429 430 431 432 |
# File 'ext/openssl/ossl_x509cert.c', line 420
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
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 |
# File 'ext/openssl/ossl_x509cert.c', line 438
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) ⇒ Object
349 350 351 352 353 354 355 356 357 358 |
# File '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 ⇒ Object
498 499 500 501 502 503 504 505 506 507 508 509 510 |
# File 'ext/openssl/ossl_x509cert.c', line 498
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) ⇒ Object
516 517 518 519 520 521 522 523 524 525 526 527 528 |
# File 'ext/openssl/ossl_x509cert.c', line 516
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
297 298 299 300 301 302 303 304 305 |
# File 'ext/openssl/ossl_x509cert.c', line 297
static VALUE
ossl_x509_get_serial(VALUE self)
{
X509 *x509;
GetX509(self, x509);
return asn1integer_to_num(X509_get_serialNumber(x509));
}
|
#serial=(integer) ⇒ Integer
311 312 313 314 315 316 317 318 319 320 |
# File 'ext/openssl/ossl_x509cert.c', line 311
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
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 |
# File 'ext/openssl/ossl_x509cert.c', line 534
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
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'ext/openssl/ossl_x509cert.c', line 326
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 ⇒ Object
350 351 352 353 354 355 356 357 358 359 360 361 362 |
# File 'ext/openssl/ossl_x509cert.c', line 350
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) ⇒ Object
368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'ext/openssl/ossl_x509cert.c', line 368
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
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'ext/openssl/ossl_x509cert.c', line 167
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 Also known as: to_s
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'ext/openssl/ossl_x509cert.c', line 191
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_text ⇒ String
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'ext/openssl/ossl_x509cert.c', line 215
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) ⇒ Object
Verifies the signature of the certificate, with the public key key. key must be an instance of OpenSSL::PKey.
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
# File 'ext/openssl/ossl_x509cert.c', line 558
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);
}
}
|
#version ⇒ Integer
262 263 264 265 266 267 268 269 270 |
# File 'ext/openssl/ossl_x509cert.c', line 262
static VALUE
ossl_x509_get_version(VALUE self)
{
X509 *x509;
GetX509(self, x509);
return LONG2NUM(X509_get_version(x509));
}
|
#version=(integer) ⇒ Integer
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'ext/openssl/ossl_x509cert.c', line 276
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;
}
|