You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:43:10