I have test keys generated like here:
openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem
openssl ec -in vapid_private.pem -pubout -out vapid_public.pem
openssl ec -in vapid_private.pem -pubout -outform DER | tail -c 65 | base64 | tr -d '=' | tr '/+' '_-' > vapid_public.der
openssl ec -in vapid_private.pem -outform DER | tail -c +8 | head -c 32 | base64 | tr -d '=' | tr '/+' '_-' > vapid_private.der
vapid_private.pem
:
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICBP2ZDHFWjIfnJXDCXFMIQIXRwQofx531ikc2R3RK+qoAoGCCqGSM49
AwEHoUQDQgAEtHLtsp1L1Jaqj/IEueEvp9yvG3wg1POAlzEf77NsM9qTMFBQiBPj
Bu8tzN5lLXnjKMy4I+AATyOI7Kz2C8RVWg==
-----END EC PRIVATE KEY-----
vapid_public.pem
:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtHLtsp1L1Jaqj/IEueEvp9yvG3wg
1POAlzEf77NsM9qTMFBQiBPjBu8tzN5lLXnjKMy4I+AATyOI7Kz2C8RVWg==
-----END PUBLIC KEY-----
vapid_private.pem
:
IE_ZkMcVaMh-clcMJcUwhAhdHBCh_HnfWKRzZHdEr6o
vapid_public.pem
:
BLRy7bKdS9SWqo_yBLnhL6fcrxt8INTzgJcxH--zbDPakzBQUIgT4wbvLczeZS154yjMuCPgAE8jiOys9gvEVVo
Then I have converter class that handles conversion. 3 out of 4 conversions work properly, I can't get private DER→PEM to work. This is minified (stripped error handling etc.) function that does it:
function convertECPrivateKeyFromDERtoPEM(string $der, string $passphrase = null): string
{
$fullDer = "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00" . self::base64_decode_url($der);
return "-----BEGIN EC PRIVATE KEY-----\n" . chunk_split(base64_encode($fullDer), 64, "\n") . "-----END EC PRIVATE KEY-----";
}
This approach works properly for public key (of course with different header+footer), it was implemented earlier by other developer. I need to add support for private keys and I struggle to get proper PEM key. Mentioned function returns PEM-like key, but it's not valid Elliptic Curve key - it can't be loaded with openssl_pkey_get_private()
.
I've looked at some libraries, but:
simplito/elliptic-php
: I don't see support for generating PEM, so it would require additional code around the librarymdanter/ecc
: it does not load raw DER key, I need to manually add ASN1 header so it's not any better than current approachweb-token/jwt-framework
: it hasJWKFactory::createFromKey()
which tries to parse DER and then PEM as a fallback, but when I pass DER key I getInvalidArgumentException: Unable to load the key
The exact same key pair can be properly converted using PERL:
perl -E 'use Crypt::PK::ECC; use MIME::Base64;my $k = Crypt::PK::ECC->new->import_key_raw(MIME::Base64::decode_base64url("IE_ZkMcVaMh-clcMJcUwhAhdHBCh_HnfWKRzZHdEr6o"), "prime256v1"); say $k->export_key_pem("private_short")'
and it's possible to get exact same PEM key like the one from which DER was generated, so it definitely is possible, but I can't get PHP to do it.
Errors from OpenSSL:
error:0D07209B:asn1 encoding routines:ASN1_get_object:too long
error:0D068066:asn1 encoding routines:asn1_check_tlen:bad object header
error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error
error:10092010:elliptic curve routines:d2i_ECPrivateKey:EC lib
error:100DE08E:elliptic curve routines:old_ec_priv_decode:decode error
error:0D07209B:asn1 encoding routines:ASN1_get_object:too long
error:0D068066:asn1 encoding routines:asn1_check_tlen:bad object header
error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error
error:0907B00D:PEM routines:PEM_read_bio_PrivateKey:ASN1 lib
I'm not so much into cryptography so I find it hard to get solution. Thanks in advance for any help!