ext/openssl/ossl_pkey.c in openssl-2.1.4 vs ext/openssl/ossl_pkey.c in openssl-2.2.0
- old
+ new
@@ -165,25 +165,31 @@
rb_scan_args(argc, argv, "11", &data, &pass);
pass = ossl_pem_passwd_value(pass);
bio = ossl_obj2bio(&data);
- if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) {
- OSSL_BIO_reset(bio);
- if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass))) {
- OSSL_BIO_reset(bio);
- if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) {
- OSSL_BIO_reset(bio);
- pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, (void *)pass);
- }
- }
- }
+ if ((pkey = d2i_PrivateKey_bio(bio, NULL)))
+ goto ok;
+ OSSL_BIO_reset(bio);
+ if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, (void *)pass)))
+ goto ok;
+ OSSL_BIO_reset(bio);
+ if ((pkey = d2i_PUBKEY_bio(bio, NULL)))
+ goto ok;
+ OSSL_BIO_reset(bio);
+ /* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */
+ if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass)))
+ goto ok;
+ OSSL_BIO_reset(bio);
+ if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)))
+ goto ok;
BIO_free(bio);
- if (!pkey)
- ossl_raise(ePKeyError, "Could not parse PKey");
+ ossl_raise(ePKeyError, "Could not parse PKey");
+ok:
+ BIO_free(bio);
return ossl_pkey_new(pkey);
}
void
ossl_pkey_check_public_key(const EVP_PKEY *pkey)
@@ -292,10 +298,164 @@
}
return self;
}
/*
+ * call-seq:
+ * pkey.oid -> string
+ *
+ * Returns the short name of the OID associated with _pkey_.
+ */
+static VALUE
+ossl_pkey_oid(VALUE self)
+{
+ EVP_PKEY *pkey;
+ int nid;
+
+ GetPKey(self, pkey);
+ nid = EVP_PKEY_id(pkey);
+ return rb_str_new_cstr(OBJ_nid2sn(nid));
+}
+
+/*
+ * call-seq:
+ * pkey.inspect -> string
+ *
+ * Returns a string describing the PKey object.
+ */
+static VALUE
+ossl_pkey_inspect(VALUE self)
+{
+ EVP_PKEY *pkey;
+ int nid;
+
+ GetPKey(self, pkey);
+ nid = EVP_PKEY_id(pkey);
+ return rb_sprintf("#<%"PRIsVALUE":%p oid=%s>",
+ rb_class_name(CLASS_OF(self)), (void *)self,
+ OBJ_nid2sn(nid));
+}
+
+static VALUE
+do_pkcs8_export(int argc, VALUE *argv, VALUE self, int to_der)
+{
+ EVP_PKEY *pkey;
+ VALUE cipher, pass;
+ const EVP_CIPHER *enc = NULL;
+ BIO *bio;
+
+ GetPKey(self, pkey);
+ rb_scan_args(argc, argv, "02", &cipher, &pass);
+ if (argc > 0) {
+ /*
+ * TODO: EncryptedPrivateKeyInfo actually has more options.
+ * Should they be exposed?
+ */
+ enc = ossl_evp_get_cipherbyname(cipher);
+ pass = ossl_pem_passwd_value(pass);
+ }
+
+ bio = BIO_new(BIO_s_mem());
+ if (!bio)
+ ossl_raise(ePKeyError, "BIO_new");
+ if (to_der) {
+ if (!i2d_PKCS8PrivateKey_bio(bio, pkey, enc, NULL, 0,
+ ossl_pem_passwd_cb, (void *)pass)) {
+ BIO_free(bio);
+ ossl_raise(ePKeyError, "i2d_PKCS8PrivateKey_bio");
+ }
+ }
+ else {
+ if (!PEM_write_bio_PKCS8PrivateKey(bio, pkey, enc, NULL, 0,
+ ossl_pem_passwd_cb, (void *)pass)) {
+ BIO_free(bio);
+ ossl_raise(ePKeyError, "PEM_write_bio_PKCS8PrivateKey");
+ }
+ }
+ return ossl_membio2str(bio);
+}
+
+/*
+ * call-seq:
+ * pkey.private_to_der -> string
+ * pkey.private_to_der(cipher, password) -> string
+ *
+ * Serializes the private key to DER-encoded PKCS #8 format. If called without
+ * arguments, unencrypted PKCS #8 PrivateKeyInfo format is used. If called with
+ * a cipher name and a password, PKCS #8 EncryptedPrivateKeyInfo format with
+ * PBES2 encryption scheme is used.
+ */
+static VALUE
+ossl_pkey_private_to_der(int argc, VALUE *argv, VALUE self)
+{
+ return do_pkcs8_export(argc, argv, self, 1);
+}
+
+/*
+ * call-seq:
+ * pkey.private_to_pem -> string
+ * pkey.private_to_pem(cipher, password) -> string
+ *
+ * Serializes the private key to PEM-encoded PKCS #8 format. See #private_to_der
+ * for more details.
+ */
+static VALUE
+ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self)
+{
+ return do_pkcs8_export(argc, argv, self, 0);
+}
+
+static VALUE
+do_spki_export(VALUE self, int to_der)
+{
+ EVP_PKEY *pkey;
+ BIO *bio;
+
+ GetPKey(self, pkey);
+ bio = BIO_new(BIO_s_mem());
+ if (!bio)
+ ossl_raise(ePKeyError, "BIO_new");
+ if (to_der) {
+ if (!i2d_PUBKEY_bio(bio, pkey)) {
+ BIO_free(bio);
+ ossl_raise(ePKeyError, "i2d_PUBKEY_bio");
+ }
+ }
+ else {
+ if (!PEM_write_bio_PUBKEY(bio, pkey)) {
+ BIO_free(bio);
+ ossl_raise(ePKeyError, "PEM_write_bio_PUBKEY");
+ }
+ }
+ return ossl_membio2str(bio);
+}
+
+/*
+ * call-seq:
+ * pkey.public_to_der -> string
+ *
+ * Serializes the public key to DER-encoded X.509 SubjectPublicKeyInfo format.
+ */
+static VALUE
+ossl_pkey_public_to_der(VALUE self)
+{
+ return do_spki_export(self, 1);
+}
+
+/*
+ * call-seq:
+ * pkey.public_to_pem -> string
+ *
+ * Serializes the public key to PEM-encoded X.509 SubjectPublicKeyInfo format.
+ */
+static VALUE
+ossl_pkey_public_to_pem(VALUE self)
+{
+ return do_spki_export(self, 0);
+}
+
+/*
* call-seq:
* pkey.sign(digest, data) -> String
*
* To sign the String _data_, _digest_, an instance of OpenSSL::Digest, must
* be provided. The return value is again a String containing the signature.
@@ -304,11 +464,11 @@
* outcome, the digest instance is reset to its initial state during the
* operation.
*
* == Example
* data = 'Sign me!'
- * digest = OpenSSL::Digest::SHA256.new
+ * digest = OpenSSL::Digest.new('SHA256')
* pkey = OpenSSL::PKey::RSA.new(2048)
* signature = pkey.sign(digest, data)
*/
static VALUE
ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
@@ -358,11 +518,11 @@
* outcome, the digest instance is reset to its initial state during the
* operation.
*
* == Example
* data = 'Sign me!'
- * digest = OpenSSL::Digest::SHA256.new
+ * digest = OpenSSL::Digest.new('SHA256')
* pkey = OpenSSL::PKey::RSA.new(2048)
* signature = pkey.sign(digest, data)
* pub_key = pkey.public_key
* puts pub_key.verify(digest, signature, data) # => true
*/
@@ -489,9 +649,15 @@
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);
+ rb_define_method(cPKey, "oid", ossl_pkey_oid, 0);
+ rb_define_method(cPKey, "inspect", ossl_pkey_inspect, 0);
+ rb_define_method(cPKey, "private_to_der", ossl_pkey_private_to_der, -1);
+ rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1);
+ rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0);
+ rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0);
rb_define_method(cPKey, "sign", ossl_pkey_sign, 2);
rb_define_method(cPKey, "verify", ossl_pkey_verify, 3);
id_private_q = rb_intern("private?");