PHP实现椭圆曲线DER私钥转PEM格式无效问题排查
prime256v1椭圆曲线VAPID密钥DER转PEM故障排查
密钥生成方式
参考web-push-php文档中VAPID认证章节的方法生成prime256v1椭圆曲线测试密钥对,使用的openssl命令如下:
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(PEM格式私钥):
-----BEGIN EC PRIVATE KEY----- MHcCAQEEICBP2ZDHFWjIfnJXDCXFMIQIXRwQofx531ikc2R3RK+qoAoGCCqGSM49 AwEHoUQDQgAEtHLtsp1L1Jaqj/IEueEvp9yvG3wg1POAlzEf77NsM9qTMFBQiBPj Bu8tzN5lLXnjKMy4I+AATyOI7Kz2C8RVWg== -----END EC PRIVATE KEY-----
vapid_public.pem(PEM格式公钥):
-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtHLtsp1L1Jaqj/IEueEvp9yvG3wg 1POAlzEf77NsM9qTMFBQiBPjBu8tzN5lLXnjKMy4I+AATyOI7Kz2C8RVWg== -----END PUBLIC KEY-----
vapid_private.der(URL安全Base64编码raw DER私钥):
IE_ZkMcVaMh-clcMJcUwhAhdHBCh_HnfWKRzZHdEr6o
vapid_public.der(URL安全Base64编码raw DER公钥):
BLRy7bKdS9SWqo_yBLnhL6fcrxt8INTzgJcxH--zbDPakzBQUIgT4wbvLczeZS154yjMuCPgAE8jiOys9gvEVVo
故障描述
编写密钥转换类处理格式转换,目前4类转换场景中有3类可正常运行,唯独DER格式私钥转PEM格式的功能始终失效。当前精简版转换函数(已剥离错误处理逻辑)如下:
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-----"; }
拼接ASN.1前缀再封装PEM头尾的方案在公钥转换场景下可正常运行(仅需替换对应的PEM头尾与ASN.1前缀),是此前其他开发者实现的公钥转换逻辑。在此基础上扩展私钥转换支持时,函数返回的类PEM结构内容无法被openssl_pkey_get_private()识别为合法椭圆曲线密钥。
现有方案调研结果
调研多个现有PHP加密库,均未找到可直接使用的方案:
simplito/elliptic-php:未提供PEM格式生成能力,需要基于库能力额外编写大量封装代码mdanter/ecc:无法直接加载原始DER格式密钥,仍需手动拼接ASN.1头,和现有实现方案相比没有优势web-token/jwt-framework:提供的JWKFactory::createFromKey()方法会优先尝试解析DER格式、失败后回退解析PEM格式,但传入DER私钥时会抛出InvalidArgumentException: Unable to load the key异常
使用Perl语言的Crypt::PK::ECC库可将同一份raw DER私钥正确转换为和原始openssl生成结果完全一致的PEM私钥,对应命令如下:
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")'
该结果证明转换需求完全可实现,但始终无法写出可正常运行的PHP实现。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
因对密码学相关的ASN.1编码、密钥结构知识了解不深,难以独立定位问题,需要可行的解决方案。
内容的提问来源于stack exchange,提问作者Wirone
相关产品推荐
相关产品推荐

