Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions RSA.xs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,29 @@ static RSA* _load_pkcs8_der_key(BIO* bio, const char* passphrase)
EVP_PKEY_free(pkey);
return rsa;
}

/* Pre-3.x helper for loading unencrypted PKCS#8 DER private keys.
d2i_RSAPrivateKey_bio only handles PKCS#1 format; unencrypted
PKCS#8 (PrivateKeyInfo) requires d2i_PKCS8_PRIV_KEY_INFO_bio. */
static RSA* _load_pkcs8_unenc_der_key(BIO* bio)
{
PKCS8_PRIV_KEY_INFO *p8inf;
EVP_PKEY* pkey;
RSA* rsa;

p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
if (!p8inf)
return NULL;

pkey = EVP_PKCS82PKEY(p8inf);
PKCS8_PRIV_KEY_INFO_free(p8inf);
if (!pkey)
return NULL;

rsa = EVP_PKEY_get1_RSA(pkey);
EVP_PKEY_free(pkey);
return rsa;
}
#endif

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
Expand Down Expand Up @@ -695,6 +718,13 @@ _new_private_key_der(proto, key_string_SV, passphrase_SV=&PL_sv_undef)
pkey = _load_pkcs8_der_key(bio, passphrase);
} else {
pkey = d2i_RSAPrivateKey_bio(bio, NULL);
if (!pkey) {
ERR_clear_error();
BIO_free(bio);
bio = BIO_new_mem_buf(keyString, keyStringLength);
if (bio)
pkey = _load_pkcs8_unenc_der_key(bio);
}
}
#endif
BIO_free(bio);
Expand Down
28 changes: 27 additions & 1 deletion t/der.t
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use Crypt::OpenSSL::RSA;

use File::Temp qw(tempfile);

BEGIN { plan tests => 30 }
BEGIN { plan tests => 35 }

# --- Generate a key pair for testing ---

Expand Down Expand Up @@ -125,6 +125,32 @@ eval { Crypt::OpenSSL::RSA->new_public_key("") };
like( $@, qr/unrecognized key format/,
"new_public_key gives helpful error on empty string" );

# --- Unencrypted PKCS#8 DER private key ---
# get_private_key_pkcs8_string() without passphrase produces unencrypted PKCS#8 PEM.
# Converting to DER gives PrivateKeyInfo (not EncryptedPrivateKeyInfo).
# On pre-3.x, this requires d2i_PKCS8_PRIV_KEY_INFO_bio, not d2i_RSAPrivateKey_bio.

my $pkcs8_pem = $rsa->get_private_key_pkcs8_string();
my $pkcs8_der = pem_to_der($pkcs8_pem);

is( ord(substr($pkcs8_der, 0, 1)), 0x30,
"Unencrypted PKCS#8 DER starts with SEQUENCE tag" );

my $priv_from_pkcs8_der;
ok( $priv_from_pkcs8_der = Crypt::OpenSSL::RSA->new_private_key($pkcs8_der),
"new_private_key loads unencrypted PKCS#8 DER" );

ok( $priv_from_pkcs8_der->is_private(),
"Unencrypted PKCS#8 DER-loaded key is private" );

is( $priv_from_pkcs8_der->get_public_key_x509_string(), $x509_pem,
"Unencrypted PKCS#8 DER key exports same public key as original" );

$priv_from_pkcs8_der->use_sha256_hash();
my $sig_pkcs8 = $priv_from_pkcs8_der->sign($plaintext);
ok( $pub_from_x509_der->verify($plaintext, $sig_pkcs8),
"Signature from unencrypted PKCS#8 DER-loaded key verifies" );

# --- Encrypted PKCS#8 DER private key with passphrase ---

my $passphrase = 'test_der_pass';
Expand Down
Loading