PHP中使用JWK私钥生成JWT令牌遇阻,求JWK转PEM解决方案
PHP中JWK转PEM的可行方案及问题解决
1. 直接用Firebase JWT处理JWK(无需手动转PEM)
Firebase JWT库本身支持直接传入JWK数组生成令牌,不用先转PEM,这是最简便的解决方式。你之前的代码错误地传入了base64编码的JWK字符串,应该改用解析后的数组:
use Firebase\JWT\JWT; public static function createJwtToken() { $jwkPrivateKey = 'eyJrdHkiOiJSU0EiLCJuIjoibWFRZk1RYjNHUTdOWENVX3JmeHJRUE9qcURhNGd6WEtwZ1dnVjhHVTJIcVZkVHQwYkIwa1NXcVJnMlpkNWNuT005TVFDakIwVEZaRU9BcjFJTFpkQVFDMEV6UXBndzZ2czlFeGlHWW1jd2dyM19mVUlzaGllUHNzWC16TmxjVG4tRmZWU21MekdiZUhuYUc1cG5HQ01udW1wTGwxNEhLVndmdGNmSnV0eHR1d3o3YXZ5NHZTSTR1Q2xwVUVtTEJ6VG93bUdXOC1MdjF3YjEwSVhoYWZ3cTE5SDZ5VjAxc0JZYU94bUY1d1FvY2NvSlp3UWxsc3ZhU2dYcV84S1lLV3c2RnZPeHlPNnI4bzJoQzJMdExYa0RiRF9XSkh1d2ZYbUZZY2JSdWQzUjhwcEt5eDlMaWZLM212Sm9va3ozU2VFVU1HVEx5Q052WHpBbFpSSUN3N3F3IiwiZSI6IkFRQUIiLCJkIjoiTFFZMUk5TjBOeklveVQtYk5UdjdIRm9wMUNKUkdSWjY4QU5IRldDRDQ1cXMwT0dmQVEzYk0tNllmUWJUTG9OZ3U3dzJfNTFxZjNKcVpIZ0ZsTlFNVmo1a0J4cXFyWjd3cTRQNl9ZdGQ2aWp6WHB1TVhiTVdkSGk2N05PQ19jaHA2Rk9wRERjS0liZXdkT2ZQZFhEZkpEY3JReXVjV1MyTEtJRW1mZGZMYWJKRUtIaVQ0NXhQb1U0a2Y3akd2cWVQbHktblJteV9fUWcwNF9WTGRHNjdQNmJKa1F4aGZkaXY1Vlp1WktGam16QzY2MVpiNU1IUGVmZUFlejRWZzFWNDVsTTQ2bVZMM3pOSHQ5MjJqSUhZT1c2VzJrekFNV2NMWS1hT2RwRzhxbG9HNVFkLXkzTktmempSeUZWSHgzdVRUb3I1QXp4TU52dzJuRmYxWlI4d3FRIiwicCI6IjJCTF9JaEhmRmh6NWJzOXFHYktNMW1zYmlIRE1CMFZXdDBNS0RPTG1uTlpyalkyd0lsRHR2dmtKNWZic09lcld2dXR4UjFqTWNnMTF1SVhqNTZXXzc0djRGVmp1SnU1M2F0VTI1NEhmVXhfS2tneGtpRVlRZWZxanhXTXRtMGllQkVmWEkySGpMa3E2UkVtdENWOHJiNUpLZ084WldMVlFFZ0pCeXNsaEVmayIsInEiOiJ0Z2ZXeVZvMHdrdXY2d0U3NkEtTERhTzNCQ1g1Z3N6djEtcGNUcmlTLXd2MFpTYndBRHVaR05IM1dpdERUWFJ4WUQ3My1EenYteW9VLVVrSDVKYnFRanhZWUxUbm5jRzhLQnRLdmlhVm9MaF9jR2xnRmlMLVJleHI1VFA3VDA5dHFWMHp4YlNGNExTcHJVdUdSVU5jVlJBNEFqb3JoOGhHZmRySWhFV2FvOE0iLCJkcCI6IlJ4cEpOcTdtMkdJRWVzQnFTN2FEdmZOVkJoc0lLTVJhZXNmbXdNTFIzSDU2dWdRTUY5N0Z2WGc2RjZDZnI1ZGx2MEctN3hsN2ZYU01ib0l3c3dQS0cwdldBVUJZYmhzLTFMc0E3RUhDZVpvV2tPVGZHRERtTHgyWlV0VUdKdmxJZG1RbG1aRVdyeDhNR0hpZUpsMVk3OGdXR0VPVjlha0Voa2lUMmVWV3VoRSIsImRxIjoiUGlJYTNJdmY2bG92YUFYNXp0SlBZbzU1VF84dVZjbGlMTThnUnZhVnBQLUZvbWxTRHFKSk5jR2xycS1KdjR4NWpxS3Z2Wm5fU0RvckFwUTlEM2U3Nm5QeXJyX1JOM1lEcGFUN05FQ3pYakxidlNFYTNUT0FtNW1Ca3JEVm5WSzlXY3dvZWRtT3dRNnB4Qzg5VzdNTWR6U2NDWmFWZVRtUlpqQWh0WlNVbmtVIiwicWkiOiJIWVFvTXFFWjFJOUE1TlByTUlsZFRUdnNOOWhuaHlLaWFYN0VKQ0Q4bHAzVXpPcThURHZCVEw2dVdMN013RF9kOURtdXZWWWEtenVndTl0YS1jd3ZER3pwbVhwODZ6VTZGc1d6aG9LNGN3WGF4Z29ZUDRmSnAzbWI0RjY5cm5zSDBzTGlUbERIaUxqSEhSQUJOTlZrN3ZXUElwZUF3eW5QQUJ5U2hKOXBkejAiLCJhbGciOiJSUzI1NiIsImtpZCI6InBvd2Vyc3luYy03OGY2MTAyZjFhIn0='; $privateKey = json_decode(base64_decode($jwkPrivateKey), true); if (!is_array($privateKey)) { throw new Exception('Invalid private key format.'); } $header = [ 'alg' => $privateKey['alg'], 'kid' => $privateKey['kid'], ]; $payload = [ 'sub' => 1, 'iat' => time(), 'exp' => time() + 300, ]; // 直接传入JWK数组,Firebase JWT会自动处理密钥验证 return JWT::encode($payload, $privateKey, $header['alg'], $header['kid']); }
2. 高效的JWK转PEM工具
如果确实需要将JWK转换为PEM格式,推荐以下两个性能更优的库,避免RSAKey的超时问题:
(1)lcobucci/jwt库
该库的JWK处理逻辑经过优化,转换速度更快:
use Lcobucci\JWT\Encoding\JoseEncoder; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\Builder; // 解析JWK数据 $jwk = json_decode(base64_decode($jwkPrivateKey), true); // 直接从JWK创建密钥对象,也可获取PEM格式 $key = InMemory::plainText(json_encode($jwk)); $pem = $key->toString(); // 获取转换后的PEM字符串 // 生成JWT示例 $signer = new Sha256(); $token = (new Builder(new JoseEncoder())) ->withClaim('sub', 1) ->issuedAt(time()) ->expiresAt(time() + 300) ->withHeader('alg', 'RS256') ->withHeader('kid', $jwk['kid']) ->getToken($signer, $key);
(2)web-token/jwt-framework库
这是你之前使用的Jose\Component所属的官方库,优化使用方式可解决超时:
use Jose\Component\Core\JWK; use Jose\Component\Core\Util\RSAKey; $jwkData = json_decode(base64_decode($jwkPrivateKey), true); $jwk = new JWK($jwkData); // 若需转PEM,确保PHP的OpenSSL扩展版本≥1.1.1(旧版本处理大密钥会卡顿) $rsaKey = RSAKey::createFromJWK($jwk); $pem = $rsaKey->toPEM();
3. 直接使用已转换的PEM
既然你已经通过Python得到了可用的PEM,可直接在PHP中使用,跳过转换步骤:
$pem = <<<'EOD' -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCZpB8xBvcZDs1c JT+t/GtA86OoNriDNcqmBaBXwZTYepV1O3RsHSRJapGDZl3lyc4z0xAKMHRMVkQ4 CvUgtl0BALQTNCmDDq+z0TGIZiZzCCvf99QiyGJ4+yxf7M2VxOf4V9VKYvMZt4ed obmmcYIye6akuXXgcpXB+1x8m63G27DPtq/Li9Iji4KWlQSYsHNOjCYZbz4u/XBv XQheFp/CrX0frJXTWwFho7GYXnBChxyglnBCWWy9pKBer/wpgpbDoW87HI7qvyja ELYu0teQNsP9Yke7B9eYVhxtG53dHymkrLH0uJ8rea8miiTPdJ4RQwZMvII29fMC VlEgLDurAgMBAAECggEALQY1I9N0NzIoyT+bNTv7HFop1CJRGRZ68ANHFWCD45qs 0OGfAQ3bM+6YfQbTLoNgu7w2/51qf3JqZHgFlNQMVj5kBxqqrZ7wq4P6/Ytd6ijz XpuMXbMWdHi67NOC/chp6FOpDDcKIbewdOfPdXDfJDcrQyucWS2LKIEmfdfLabJE KHiT45xPoU4kf7jGvqePly+nRmy//Qg04/VLdG67P6bJkQxhfdiv5VZuZKFjmzC6 61Zb5MHPefeAez4Vg1V45lM46mVL3zNHt922jIHYOW6W2kzAMWcLY+aOdpG8qloG 5Qd+y3NKfzjRyFVHx3uTTor5AzxMNvw2nFf1ZR8wqQKBgQDYEv8iEd8WHPluz2oZ sozWaxuIcMwHRVa3QwoM4uac1muNjbAiUO2++Qnl9uw56ta+63FHWMxyDXW4hePn pb/vi/gVWO4m7ndq1Tbngd9TH8qSDGSIRhB5+qPFYy2bSJ4ER9cjYeMuSrpESa0J XytvkkqA7xlYtVASAkHKyWER+QKBgQC2B9bJWjTCS6/rATvoD4sNo7cEJfmCzO/X 6lxOuJL7C/RlJvAAO5kY0fdaK0NNdHFgPvf4PO/7KhT5SQfklupCPFhgtOedwbwo G0q+JpWguH9waWAWIv5F7GvlM/tPT22pXTPFtIXgtKmtS4ZFQ1xVEDgCOiuHyEZ9 2siERZqjwwKBgEcaSTau5thiBHrAaku2g73zVQYbCCjEWnrH5sDC0dx+eroEDBfe xb14Ohegn6+XZb9Bvu8Ze310jG6CMLMDyhtL1gFAWG4bPtS7AOxBwnmaFpDk3xgw 5i8dmVLVBib5SHZkJZmRFq8fDBh4niZdWO/IFhhDlfWpBIZIk9nlVroRAoGAPiIa 3Ivf6lovaAX5ztJPYo55T/8uVcliLM8gRvaVpP+FomlSDqJJNcGlrq+Jv4x5jqKv vZn/SDorApQ9D3e76nPyrr/RN3YDpaT7NECzXjLbvSEa3TOAm5mBkrDVnVK9Wcwo edmOwQ6pxC89W7MMdzScCZaVeTmRZjAhtZSUnkUCgYAdhCgyoRnUj0Dk0+swiV1N O+w32GeHIqJpfsQkIPyWndTM6rxMO8FMvq5YvszAP930Oa69Vhr7O6C721r5zC8M bOmZenzrNToWxbOGgrhzBdrGChg/h8mneZvgXr2uewfSwuJOUMeIuMcdEAE01WTu 9Y8il4DDKc8AHJKEn2l3PQ== -----END PRIVATE KEY----- EOD; $header = [ 'alg' => 'RS256', 'kid' => 'powersync-78f6102f1a', // 对应JWK中的kid字段 ]; $payload = [ 'sub' => 1, 'iat' => time(), 'exp' => time() + 300, ]; return JWT::encode($payload, $pem, $header['alg'], $header['kid']);
关键问题总结
- 你之前的核心错误是
JWT::encode传入了base64编码的JWK字符串,而非解析后的数组或PEM,导致OpenSSL无法验证密钥。 RSAKey超时多因PHP的OpenSSL扩展版本过低,升级到1.1.1及以上版本可解决性能瓶颈。
内容的提问来源于stack exchange,提问作者Kelv1nG
相关产品推荐
相关产品推荐

