From cb57042beeac1f6acc424cc1339acfa198d5ac8b Mon Sep 17 00:00:00 2001 From: emboss Date: Fri, 13 May 2011 19:25:18 +0000 Subject: Sat May 14 04:19:06 2011 Martin Bosslet * NEWS: Describe altered behaviour for RSA and DSA public key encoding. [Ruby 1.9 - Bug #4421, Bug #4422] [ruby-core:35327,35328] Previous revision: 31553 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@31554 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 6 +++++ NEWS | 6 +++++ ext/openssl/ossl_digest.c | 2 ++ ext/openssl/ossl_pkey.c | 60 +++++++++++++++++++++++++++++++++---------- ext/openssl/ossl_pkey.h | 2 +- test/openssl/test_pkey_rsa.rb | 42 ++++++++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index ddf7adc6cb..ffdb43b399 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Sat May 14 04:19:06 2011 Martin Bosslet + + * NEWS: Describe altered behaviour for RSA and DSA public key + encoding. [Ruby 1.9 - Bug #4421, Bug #4422] + [ruby-core:35327,35328] + Sat May 14 02:57:52 2011 Eric Hodel * lib/ipaddr.rb (unless Socket): Document valid*? methods. Patch by diff --git a/NEWS b/NEWS index 698453b43e..1a9a14a9c3 100644 --- a/NEWS +++ b/NEWS @@ -98,6 +98,12 @@ with all sufficient information, see the ChangeLog file. * net/http * SNI (Server Name Indication) supported for HTTPS. +* openssl + * PKey::RSA and PKey::DSA now use the generic X.509 encoding scheme + (e.g. used in a X.509 certificate's Subject Public Key Info) when + exporting public keys to DER or PEM. Backward compatibility is + ensured by (already existing) fallbacks during creation. + * optparse * support for bash/zsh completion. diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index 2b76fe7a49..9f3d2697af 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -239,6 +239,8 @@ Init_ossl_digest() mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ #endif + /* Allows you to compute cryptographic hashes of arbitrary data. + */ cDigest = rb_define_class_under(mOSSL, "Digest", rb_path2class("Digest::Class")); eDigestError = rb_define_class_under(cDigest, "DigestError", eOSSLError); diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index b5047bec26..d6940acda4 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -18,6 +18,9 @@ VALUE cPKey; VALUE ePKeyError; ID id_private_q; +#define reset_bio(b) (void)BIO_reset((b)); \ + (void)ERR_get_error(); + /* * callback for generating keys */ @@ -65,23 +68,50 @@ ossl_pkey_new(EVP_PKEY *pkey) return Qnil; /* not reached */ } -VALUE -ossl_pkey_new_from_file(VALUE filename) +/* + * call-seq: + * OpenSSL::PKey.read(string [, pwd ] ) -> PKey + * OpenSSL::PKey.read(file [, pwd ]) -> PKey + * + * === Parameters + * * +string+ is a DER- or PEM-encoded string containing an arbitrary private + * or public key. + * * +file+ is an instance of +File+ containing a DER- or PEM-encoded + * arbitrary private or public key. + * * +pwd+ is an optional password in case +string+ or +file+ is an encrypted + * PEM resource. + */ +VALUE +ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) { - FILE *fp; EVP_PKEY *pkey; - - SafeStringValue(filename); - if (!(fp = fopen(RSTRING_PTR(filename), "r"))) { - ossl_raise(ePKeyError, "%s", strerror(errno)); + BIO *bio; + VALUE data, pass; + char *passwd = NULL; + + rb_scan_args(argc, argv, "11", &data, &pass); + + bio = ossl_obj2bio(data); + if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) { + reset_bio(bio); + if (!NIL_P(pass)) { + passwd = StringValuePtr(pass); + } + if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, passwd))) { + reset_bio(bio); + if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) { + reset_bio(bio); + if (!NIL_P(pass)) { + passwd = StringValuePtr(pass); + } + pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, passwd); + } + } } - - pkey = PEM_read_PrivateKey(fp, NULL, ossl_pem_passwd_cb, NULL); - fclose(fp); - if (!pkey) { - ossl_raise(ePKeyError, NULL); - } - + + BIO_free(bio); + if (!pkey) + ossl_raise(rb_eArgError, "Could not parse PKey"); return ossl_pkey_new(pkey); } @@ -221,6 +251,8 @@ Init_ossl_pkey() cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); + rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1); + rb_define_alloc_func(cPKey, ossl_pkey_alloc); rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0); diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index 5e3329d326..4b19164585 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -41,7 +41,7 @@ extern ID id_private_q; void ossl_generate_cb(int, int, void *); VALUE ossl_pkey_new(EVP_PKEY *); -VALUE ossl_pkey_new_from_file(VALUE); +VALUE ossl_pkey_new_from_data(int, VALUE *, VALUE); EVP_PKEY *GetPKeyPtr(VALUE); EVP_PKEY *DupPKeyPtr(VALUE); EVP_PKEY *GetPrivPKeyPtr(VALUE); diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb index f42748ed22..6a1f9e79d1 100644 --- a/test/openssl/test_pkey_rsa.rb +++ b/test/openssl/test_pkey_rsa.rb @@ -46,6 +46,48 @@ class OpenSSL::TestPKeyRSA < Test::Unit::TestCase OpenSSL::PKey::RSA.new pem assert_equal([], OpenSSL.errors) end + + def test_read_private_key_der + der = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_der + key = OpenSSL::PKey.read(der) + assert(key.private?) + assert_equal(der, key.to_der) + end + + def test_read_private_key_pem + pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem + key = OpenSSL::PKey.read(pem) + assert(key.private?) + assert_equal(pem, key.to_pem) + end + + def test_read_public_key_der + der = OpenSSL::TestUtils::TEST_KEY_RSA1024.public_key.to_der + key = OpenSSL::PKey.read(der) + assert(!key.private?) + assert_equal(der, key.to_der) + end + + def test_read_public_key_pem + pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.public_key.to_pem + key = OpenSSL::PKey.read(pem) + assert(!key.private?) + assert_equal(pem, key.to_pem) + end + + def test_read_private_key_pem_pw + pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret') + #callback form for password + key = OpenSSL::PKey.read(pem) do + 'secret' + end + assert(key.private?) + # pass password directly + key = OpenSSL::PKey.read(pem, 'secret') + assert(key.private?) + #omit pem equality check, will be different due to cipher iv + end + end end -- cgit v1.2.3