mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	[ruby/openssl] Implement Certificate.load to load certificate chain. (https://github.com/ruby/openssl/pull/441)
				
					
				
			* Add feature for loading the chained certificate into Certificate array.
05e1c015d6
Co-authored-by: Sao I Kuan <saoikuan@gmail.com>
			
			
This commit is contained in:
		
							parent
							
								
									a01daab656
								
							
						
					
					
						commit
						1146a94aee
					
				
					 8 changed files with 246 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -338,6 +338,10 @@ module OpenSSL
 | 
			
		|||
          q.text 'not_after='; q.pp self.not_after
 | 
			
		||||
        }
 | 
			
		||||
      end
 | 
			
		||||
      
 | 
			
		||||
      def self.load_file(path)
 | 
			
		||||
        load(File.binread(path))
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    class CRL
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -707,6 +707,157 @@ ossl_x509_eq(VALUE self, VALUE other)
 | 
			
		|||
    return !X509_cmp(a, b) ? Qtrue : Qfalse;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct load_chained_certificates_arguments {
 | 
			
		||||
    VALUE certificates;
 | 
			
		||||
    X509 *certificate;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
load_chained_certificates_append_push(VALUE _arguments) {
 | 
			
		||||
    struct load_chained_certificates_arguments *arguments = (struct load_chained_certificates_arguments*)_arguments;
 | 
			
		||||
 | 
			
		||||
    if (arguments->certificates == Qnil) {
 | 
			
		||||
        arguments->certificates = rb_ary_new();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rb_ary_push(arguments->certificates, ossl_x509_new(arguments->certificate));
 | 
			
		||||
 | 
			
		||||
    return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
load_chained_certificate_append_ensure(VALUE _arguments) {
 | 
			
		||||
    struct load_chained_certificates_arguments *arguments = (struct load_chained_certificates_arguments*)_arguments;
 | 
			
		||||
 | 
			
		||||
    X509_free(arguments->certificate);
 | 
			
		||||
 | 
			
		||||
    return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline static VALUE
 | 
			
		||||
load_chained_certificates_append(VALUE certificates, X509 *certificate) {
 | 
			
		||||
    struct load_chained_certificates_arguments arguments;
 | 
			
		||||
    arguments.certificates = certificates;
 | 
			
		||||
    arguments.certificate = certificate;
 | 
			
		||||
 | 
			
		||||
    rb_ensure(load_chained_certificates_append_push, (VALUE)&arguments, load_chained_certificate_append_ensure, (VALUE)&arguments);
 | 
			
		||||
    
 | 
			
		||||
    return arguments.certificates;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
load_chained_certificates_PEM(BIO *in) {
 | 
			
		||||
    VALUE certificates = Qnil;
 | 
			
		||||
    X509 *certificate = PEM_read_bio_X509(in, NULL, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    /* If we cannot read even one certificate: */
 | 
			
		||||
    if (certificate == NULL) {
 | 
			
		||||
        /* If we cannot read one certificate because we could not read the PEM encoding: */
 | 
			
		||||
        if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
 | 
			
		||||
            ossl_clear_error();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ERR_peek_last_error())
 | 
			
		||||
            ossl_raise(eX509CertError, NULL);
 | 
			
		||||
        else
 | 
			
		||||
            return Qnil;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    certificates = load_chained_certificates_append(Qnil, certificate);
 | 
			
		||||
 | 
			
		||||
    while ((certificate = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
 | 
			
		||||
      load_chained_certificates_append(certificates, certificate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* We tried to read one more certificate but could not read start line: */
 | 
			
		||||
    if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
 | 
			
		||||
        /* This is not an error, it means we are finished: */
 | 
			
		||||
        ossl_clear_error();
 | 
			
		||||
 | 
			
		||||
        return certificates;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Alternatively, if we reached the end of the file and there was no error: */
 | 
			
		||||
    if (BIO_eof(in) && !ERR_peek_last_error()) {
 | 
			
		||||
        return certificates;
 | 
			
		||||
    } else {
 | 
			
		||||
        /* Otherwise, we tried to read a certificate but failed somewhere: */
 | 
			
		||||
        ossl_raise(eX509CertError, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
load_chained_certificates_DER(BIO *in) {
 | 
			
		||||
    X509 *certificate = d2i_X509_bio(in, NULL);
 | 
			
		||||
 | 
			
		||||
    /* If we cannot read one certificate: */
 | 
			
		||||
    if (certificate == NULL) {
 | 
			
		||||
        /* Ignore error. We could not load. */
 | 
			
		||||
        ossl_clear_error();
 | 
			
		||||
 | 
			
		||||
        return Qnil;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return load_chained_certificates_append(Qnil, certificate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
load_chained_certificates(VALUE _io) {
 | 
			
		||||
    BIO *in = (BIO*)_io;
 | 
			
		||||
    VALUE certificates = Qnil;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
      DER is a binary format and it may contain octets within it that look like
 | 
			
		||||
      PEM encoded certificates. So we need to check DER first.
 | 
			
		||||
    */
 | 
			
		||||
    certificates = load_chained_certificates_DER(in);
 | 
			
		||||
 | 
			
		||||
    if (certificates != Qnil)
 | 
			
		||||
        return certificates;
 | 
			
		||||
 | 
			
		||||
    OSSL_BIO_reset(in);
 | 
			
		||||
 | 
			
		||||
    certificates = load_chained_certificates_PEM(in);
 | 
			
		||||
 | 
			
		||||
    if (certificates != Qnil)
 | 
			
		||||
        return certificates;
 | 
			
		||||
 | 
			
		||||
    /* Otherwise we couldn't read the output correctly so fail: */
 | 
			
		||||
    ossl_raise(eX509CertError, "Could not detect format of certificate data!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
load_chained_certificates_ensure(VALUE _io) {
 | 
			
		||||
    BIO *in = (BIO*)_io;
 | 
			
		||||
 | 
			
		||||
    BIO_free(in);
 | 
			
		||||
 | 
			
		||||
    return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * call-seq:
 | 
			
		||||
 *    OpenSSL::X509::Certificate.load(string) -> [certs...]
 | 
			
		||||
 *    OpenSSL::X509::Certificate.load(file) -> [certs...]
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * INIT
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -815,6 +966,8 @@ Init_ossl_x509cert(void)
 | 
			
		|||
     */
 | 
			
		||||
    cX509Cert = rb_define_class_under(mX509, "Certificate", rb_cObject);
 | 
			
		||||
 | 
			
		||||
    rb_define_singleton_method(cX509Cert, "load", ossl_x509_load, 1);
 | 
			
		||||
 | 
			
		||||
    rb_define_alloc_func(cX509Cert, ossl_x509_alloc);
 | 
			
		||||
    rb_define_method(cX509Cert, "initialize", ossl_x509_initialize, -1);
 | 
			
		||||
    rb_define_method(cX509Cert, "initialize_copy", ossl_x509_copy, 1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										
											BIN
										
									
								
								test/openssl/fixtures/pkey/certificate.der
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/openssl/fixtures/pkey/certificate.der
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										0
									
								
								test/openssl/fixtures/pkey/empty.der
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								test/openssl/fixtures/pkey/empty.der
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								test/openssl/fixtures/pkey/empty.pem
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								test/openssl/fixtures/pkey/empty.pem
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										56
									
								
								test/openssl/fixtures/pkey/fullchain.pem
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								test/openssl/fixtures/pkey/fullchain.pem
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIFKTCCBBGgAwIBAgISBFspP+tJfRaC6xprreB4Rp9KMA0GCSqGSIb3DQEBCwUA
 | 
			
		||||
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
 | 
			
		||||
EwJSMzAeFw0yMTA0MTcwMjQzMTlaFw0yMTA3MTYwMjQzMTlaMBwxGjAYBgNVBAMT
 | 
			
		||||
EXd3dy5jb2Rlb3Rha3UuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
 | 
			
		||||
AQEAx6h5vNPfkkrtYWxn1PWDDLRAwrGmZbkYPttjHBRSwTcd7rsIX4PcSzw9fWxm
 | 
			
		||||
K4vIkAYoKAElIvsSE3xRUjyzMrACfdhK5J8rG25fq94iVyoYaNBQV0WMJkO6X47s
 | 
			
		||||
hGeIKkK91ohR5b2tMw3/z9zELP0TVo2TPG7rYsBZm34myldqDA8yVEBEOa+Qdpda
 | 
			
		||||
9xewPhkkdpAU55qgWTrD21m7vGq9WpsBz4wNKnwVsaugtkRH82VPIfaL4ZI9kox6
 | 
			
		||||
QoPWe/tHUBdlDkuT7ud77eLAWnC/5Clg28/9GU/Z8Nj8SrrKuXL6WUXmxxaAhWUR
 | 
			
		||||
Qx4VblZeuIpwd0nHyP0hz4CWKQIDAQABo4ICTTCCAkkwDgYDVR0PAQH/BAQDAgWg
 | 
			
		||||
MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0G
 | 
			
		||||
A1UdDgQWBBTKiSGZuLFSIG2JPbFSZa9TxMu5WTAfBgNVHSMEGDAWgBQULrMXt1hW
 | 
			
		||||
y65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6
 | 
			
		||||
Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iu
 | 
			
		||||
b3JnLzAcBgNVHREEFTATghF3d3cuY29kZW90YWt1LmNvbTBMBgNVHSAERTBDMAgG
 | 
			
		||||
BmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3Bz
 | 
			
		||||
LmxldHNlbmNyeXB0Lm9yZzCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB3AJQgvB6O
 | 
			
		||||
1Y1siHMfgosiLA3R2k1ebE+UPWHbTi9YTaLCAAABeN3s/lgAAAQDAEgwRgIhAKFY
 | 
			
		||||
Q+vBe3zyeBazxp8kVN7oLvcQ6Y9PPz199tVhYnEbAiEAhU/xdbQaY/6b93h+7NTF
 | 
			
		||||
sPG7X4lq/3UoNgoXcAVGZgoAdgD2XJQv0XcwIhRUGAgwlFaO400TGTO/3wwvIAvM
 | 
			
		||||
TvFk4wAAAXjd7P5OAAAEAwBHMEUCIQDWd79+jWaGuf3acm5/yV95jL2KvzeGFfdU
 | 
			
		||||
HZlKIeWFmAIgDSZ6ug7AyhYNKjzFV4ZSICln+L4yI92EpOa+8gDG6/0wDQYJKoZI
 | 
			
		||||
hvcNAQELBQADggEBAHIhMYm06lLFmJL+cfIg5fFEmFNdHmmZn88Hypv4/MtmqTKv
 | 
			
		||||
5asF/z3TvhW4hX2+TY+NdcqGT7cZFo/ZF/tS6oBXPgmBYM1dEfp2FAdnGNOySC5Y
 | 
			
		||||
7RC4Uk9TUpP2g101YBmj6dQKQluAwIQk+gO4MSlHE0J0U/lMpjvrLWcuHbV4/xWJ
 | 
			
		||||
IdM+iPq8GeYt5epYmNc7XeRIgv7V3RxDQdBv2OVM5mtPVerdiO0ISrdbe5mvz2+Z
 | 
			
		||||
rhSg+EJNHlmMwcq5HqtMwS8M8Ax+vLmWCOkPWXhyV8wQaQcFjZJfpIGUvCnMTqsh
 | 
			
		||||
kSIYXq2CbSDUUFRFssNN6EdVms0KnmW3BUu0xAk=
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/
 | 
			
		||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
 | 
			
		||||
DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow
 | 
			
		||||
MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT
 | 
			
		||||
AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs
 | 
			
		||||
jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp
 | 
			
		||||
Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB
 | 
			
		||||
U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7
 | 
			
		||||
gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel
 | 
			
		||||
/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R
 | 
			
		||||
oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
 | 
			
		||||
BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p
 | 
			
		||||
ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE
 | 
			
		||||
p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE
 | 
			
		||||
AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu
 | 
			
		||||
Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0
 | 
			
		||||
LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf
 | 
			
		||||
r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B
 | 
			
		||||
AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH
 | 
			
		||||
ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8
 | 
			
		||||
S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL
 | 
			
		||||
qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p
 | 
			
		||||
O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw
 | 
			
		||||
UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
							
								
								
									
										1
									
								
								test/openssl/fixtures/pkey/garbage.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/openssl/fixtures/pkey/garbage.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
Hello World
 | 
			
		||||
| 
						 | 
				
			
			@ -288,6 +288,38 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
 | 
			
		|||
    assert_equal cert.to_der, deserialized.to_der
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_load_file_empty_pem
 | 
			
		||||
    empty_path = Fixtures.file_path("pkey", "empty.pem")
 | 
			
		||||
    assert_raise(OpenSSL::X509::CertificateError) do
 | 
			
		||||
      OpenSSL::X509::Certificate.load_file(empty_path)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_load_file_fullchain_pem
 | 
			
		||||
    fullchain_path = Fixtures.file_path("pkey", "fullchain.pem")
 | 
			
		||||
    certificates = OpenSSL::X509::Certificate.load_file(fullchain_path)
 | 
			
		||||
    assert_equal 2, certificates.size
 | 
			
		||||
    assert_equal "/CN=www.codeotaku.com", certificates[0].subject.to_s
 | 
			
		||||
    assert_equal "/C=US/O=Let's Encrypt/CN=R3", certificates[1].subject.to_s
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_load_file_certificate_der
 | 
			
		||||
    fullchain_path = Fixtures.file_path("pkey", "certificate.der")
 | 
			
		||||
    certificates = OpenSSL::X509::Certificate.load_file(fullchain_path)
 | 
			
		||||
 | 
			
		||||
    # DER encoding can only contain one certificate:
 | 
			
		||||
    assert_equal 1, certificates.size
 | 
			
		||||
    assert_equal "/CN=www.codeotaku.com", certificates[0].subject.to_s
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_load_file_fullchain_garbage
 | 
			
		||||
    fullchain_path = Fixtures.file_path("pkey", "garbage.txt")
 | 
			
		||||
 | 
			
		||||
    assert_raise(OpenSSL::X509::CertificateError) do
 | 
			
		||||
      certificates = OpenSSL::X509::Certificate.load_file(fullchain_path)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def certificate_error_returns_false
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue