Skip to content

Commit 2cb67d7

Browse files
authored
Merge pull request #328 from rhenium/ky/pkey-refactor-serialization
pkey: refactor PEM/DER serialization code
2 parents 413b155 + 56f0d34 commit 2cb67d7

14 files changed

+282
-514
lines changed

ext/openssl/ossl.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -679,13 +679,13 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
679679
*
680680
* A key can also be loaded from a file.
681681
*
682-
* key2 = OpenSSL::PKey::RSA.new File.read 'private_key.pem'
682+
* key2 = OpenSSL::PKey.read File.read 'private_key.pem'
683683
* key2.public? # => true
684684
* key2.private? # => true
685685
*
686686
* or
687687
*
688-
* key3 = OpenSSL::PKey::RSA.new File.read 'public_key.pem'
688+
* key3 = OpenSSL::PKey.read File.read 'public_key.pem'
689689
* key3.public? # => true
690690
* key3.private? # => false
691691
*
@@ -697,7 +697,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
697697
*
698698
* key4_pem = File.read 'private.secure.pem'
699699
* pass_phrase = 'my secure pass phrase goes here'
700-
* key4 = OpenSSL::PKey::RSA.new key4_pem, pass_phrase
700+
* key4 = OpenSSL::PKey.read key4_pem, pass_phrase
701701
*
702702
* == RSA Encryption
703703
*

ext/openssl/ossl_pkey.c

+91-39
Original file line numberDiff line numberDiff line change
@@ -95,34 +95,30 @@ const rb_data_type_t ossl_evp_pkey_type = {
9595
static VALUE
9696
pkey_new0(EVP_PKEY *pkey)
9797
{
98-
VALUE obj;
98+
VALUE klass, obj;
9999
int type;
100100

101101
if (!pkey || (type = EVP_PKEY_base_id(pkey)) == EVP_PKEY_NONE)
102102
ossl_raise(rb_eRuntimeError, "pkey is empty");
103103

104104
switch (type) {
105105
#if !defined(OPENSSL_NO_RSA)
106-
case EVP_PKEY_RSA:
107-
return ossl_rsa_new(pkey);
106+
case EVP_PKEY_RSA: klass = cRSA; break;
108107
#endif
109108
#if !defined(OPENSSL_NO_DSA)
110-
case EVP_PKEY_DSA:
111-
return ossl_dsa_new(pkey);
109+
case EVP_PKEY_DSA: klass = cDSA; break;
112110
#endif
113111
#if !defined(OPENSSL_NO_DH)
114-
case EVP_PKEY_DH:
115-
return ossl_dh_new(pkey);
112+
case EVP_PKEY_DH: klass = cDH; break;
116113
#endif
117114
#if !defined(OPENSSL_NO_EC)
118-
case EVP_PKEY_EC:
119-
return ossl_ec_new(pkey);
115+
case EVP_PKEY_EC: klass = cEC; break;
120116
#endif
121-
default:
122-
obj = NewPKey(cPKey);
123-
SetPKey(obj, pkey);
124-
return obj;
117+
default: klass = cPKey; break;
125118
}
119+
obj = NewPKey(klass);
120+
SetPKey(obj, pkey);
121+
return obj;
126122
}
127123

128124
VALUE
@@ -140,6 +136,35 @@ ossl_pkey_new(EVP_PKEY *pkey)
140136
return obj;
141137
}
142138

139+
EVP_PKEY *
140+
ossl_pkey_read_generic(BIO *bio, VALUE pass)
141+
{
142+
void *ppass = (void *)pass;
143+
EVP_PKEY *pkey;
144+
145+
if ((pkey = d2i_PrivateKey_bio(bio, NULL)))
146+
goto out;
147+
OSSL_BIO_reset(bio);
148+
if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, ppass)))
149+
goto out;
150+
OSSL_BIO_reset(bio);
151+
if ((pkey = d2i_PUBKEY_bio(bio, NULL)))
152+
goto out;
153+
OSSL_BIO_reset(bio);
154+
/* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */
155+
if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, ppass)))
156+
goto out;
157+
OSSL_BIO_reset(bio);
158+
if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)))
159+
goto out;
160+
OSSL_BIO_reset(bio);
161+
if ((pkey = PEM_read_bio_Parameters(bio, NULL)))
162+
goto out;
163+
164+
out:
165+
return pkey;
166+
}
167+
143168
/*
144169
* call-seq:
145170
* OpenSSL::PKey.read(string [, pwd ]) -> PKey
@@ -164,30 +189,11 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self)
164189
VALUE data, pass;
165190

166191
rb_scan_args(argc, argv, "11", &data, &pass);
167-
pass = ossl_pem_passwd_value(pass);
168-
169192
bio = ossl_obj2bio(&data);
170-
if ((pkey = d2i_PrivateKey_bio(bio, NULL)))
171-
goto ok;
172-
OSSL_BIO_reset(bio);
173-
if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, (void *)pass)))
174-
goto ok;
175-
OSSL_BIO_reset(bio);
176-
if ((pkey = d2i_PUBKEY_bio(bio, NULL)))
177-
goto ok;
178-
OSSL_BIO_reset(bio);
179-
/* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */
180-
if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass)))
181-
goto ok;
182-
OSSL_BIO_reset(bio);
183-
if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)))
184-
goto ok;
185-
186-
BIO_free(bio);
187-
ossl_raise(ePKeyError, "Could not parse PKey");
188-
189-
ok:
193+
pkey = ossl_pkey_read_generic(bio, ossl_pem_passwd_value(pass));
190194
BIO_free(bio);
195+
if (!pkey)
196+
ossl_raise(ePKeyError, "Could not parse PKey");
191197
return ossl_pkey_new(pkey);
192198
}
193199

@@ -335,6 +341,52 @@ ossl_pkey_inspect(VALUE self)
335341
OBJ_nid2sn(nid));
336342
}
337343

344+
VALUE
345+
ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, int to_der)
346+
{
347+
EVP_PKEY *pkey;
348+
VALUE cipher, pass;
349+
const EVP_CIPHER *enc = NULL;
350+
BIO *bio;
351+
352+
GetPKey(self, pkey);
353+
rb_scan_args(argc, argv, "02", &cipher, &pass);
354+
if (!NIL_P(cipher)) {
355+
enc = ossl_evp_get_cipherbyname(cipher);
356+
pass = ossl_pem_passwd_value(pass);
357+
}
358+
359+
bio = BIO_new(BIO_s_mem());
360+
if (!bio)
361+
ossl_raise(ePKeyError, "BIO_new");
362+
if (to_der) {
363+
if (!i2d_PrivateKey_bio(bio, pkey)) {
364+
BIO_free(bio);
365+
ossl_raise(ePKeyError, "i2d_PrivateKey_bio");
366+
}
367+
}
368+
else {
369+
#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
370+
if (!PEM_write_bio_PrivateKey_traditional(bio, pkey, enc, NULL, 0,
371+
ossl_pem_passwd_cb,
372+
(void *)pass)) {
373+
#else
374+
char pem_str[80];
375+
const char *aname;
376+
377+
EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, &aname, pkey->ameth);
378+
snprintf(pem_str, sizeof(pem_str), "%s PRIVATE KEY", aname);
379+
if (!PEM_ASN1_write_bio((i2d_of_void *)i2d_PrivateKey, pem_str, bio,
380+
pkey, enc, NULL, 0, ossl_pem_passwd_cb,
381+
(void *)pass)) {
382+
#endif
383+
BIO_free(bio);
384+
ossl_raise(ePKeyError, "PEM_write_bio_PrivateKey_traditional");
385+
}
386+
}
387+
return ossl_membio2str(bio);
388+
}
389+
338390
static VALUE
339391
do_pkcs8_export(int argc, VALUE *argv, VALUE self, int to_der)
340392
{
@@ -404,8 +456,8 @@ ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self)
404456
return do_pkcs8_export(argc, argv, self, 0);
405457
}
406458

407-
static VALUE
408-
do_spki_export(VALUE self, int to_der)
459+
VALUE
460+
ossl_pkey_export_spki(VALUE self, int to_der)
409461
{
410462
EVP_PKEY *pkey;
411463
BIO *bio;
@@ -438,7 +490,7 @@ do_spki_export(VALUE self, int to_der)
438490
static VALUE
439491
ossl_pkey_public_to_der(VALUE self)
440492
{
441-
return do_spki_export(self, 1);
493+
return ossl_pkey_export_spki(self, 1);
442494
}
443495

444496
/*
@@ -450,7 +502,7 @@ ossl_pkey_public_to_der(VALUE self)
450502
static VALUE
451503
ossl_pkey_public_to_pem(VALUE self)
452504
{
453-
return do_spki_export(self, 0);
505+
return ossl_pkey_export_spki(self, 0);
454506
}
455507

456508
/*

ext/openssl/ossl_pkey.h

+15-3
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,24 @@ void ossl_generate_cb_stop(void *ptr);
4545

4646
VALUE ossl_pkey_new(EVP_PKEY *);
4747
void ossl_pkey_check_public_key(const EVP_PKEY *);
48+
EVP_PKEY *ossl_pkey_read_generic(BIO *, VALUE);
4849
EVP_PKEY *GetPKeyPtr(VALUE);
4950
EVP_PKEY *DupPKeyPtr(VALUE);
5051
EVP_PKEY *GetPrivPKeyPtr(VALUE);
52+
53+
/*
54+
* Serializes _self_ in X.509 SubjectPublicKeyInfo format and returns the
55+
* resulting String. Sub-classes use this when overriding #to_der.
56+
*/
57+
VALUE ossl_pkey_export_spki(VALUE self, int to_der);
58+
/*
59+
* Serializes the private key _self_ in the traditional private key format
60+
* and returns the resulting String. Sub-classes use this when overriding
61+
* #to_der.
62+
*/
63+
VALUE ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self,
64+
int to_der);
65+
5166
void Init_ossl_pkey(void);
5267

5368
/*
@@ -56,7 +71,6 @@ void Init_ossl_pkey(void);
5671
extern VALUE cRSA;
5772
extern VALUE eRSAError;
5873

59-
VALUE ossl_rsa_new(EVP_PKEY *);
6074
void Init_ossl_rsa(void);
6175

6276
/*
@@ -65,7 +79,6 @@ void Init_ossl_rsa(void);
6579
extern VALUE cDSA;
6680
extern VALUE eDSAError;
6781

68-
VALUE ossl_dsa_new(EVP_PKEY *);
6982
void Init_ossl_dsa(void);
7083

7184
/*
@@ -74,7 +87,6 @@ void Init_ossl_dsa(void);
7487
extern VALUE cDH;
7588
extern VALUE eDHError;
7689

77-
VALUE ossl_dh_new(EVP_PKEY *);
7890
void Init_ossl_dh(void);
7991

8092
/*

0 commit comments

Comments
 (0)