如何在OpenSSL中使用Apple身份验证公钥?PHP调用报错排查
如何使用Apple公钥配合OpenSSL(PHP实现)
你尝试使用Apple身份验证服务的公钥配合OpenSSL,但代码运行报错,错误信息如下:
Fatal error: Uncaught TypeError: openssl_pkey_get_details(): Argument #1 ($key) must be of type OpenSSLAsymmetricKey
你的代码:
<?php $key = '-----BEGIN PUBLIC KEY-----'. " ".chunk_split("1JiU4l3YCeT4o0gVmxGTEK1IXR-Ghdg5Bzka12tzmtdCxU00ChH66aV-4HRBjF1t95IsaeHeDFRgmF0lJbTDTqa6_VZo2hc0zTiUAsGLacN6slePvDcR1IMucQGtPP5tGhIbU-HKabsKOFdD4VQ5PCXifjpN9R-1qOR571BxCAl4u1kUUIePAAJcBcqGRFSI_I1j_jbN3gflK_8ZNmgnPrXA0kZXzj1I7ZHgekGbZoxmDrzYm2zmja1MsE5A_JX7itBYnlR41LOtvLRCNtw7K3EFlbfB6hkPL-Swk5XNGbWZdTROmaTNzJhV-lWT0gGm6V1qWAK2qOZoIDa_3Ud0Gw", 64).'-----END PUBLIC KEY-----'; print_r($key); $res = openssl_pkey_get_public($key); print_r(openssl_pkey_get_details($res));
问题原因
从Apple接口获取的是JWK格式的公钥(JSON Web Key),你直接把JWK里的n值(模数值)拼接成PEM格式是错误的:
- RSA公钥需要同时包含模(n)和指数(e)两个核心参数,你的代码缺少指数部分
- base64url编码的n值需要先转换为普通base64,再解码为二进制,不能直接作为PEM内容
- 未按照RSA公钥的ASN.1结构组装,导致OpenSSL无法识别为有效公钥,
openssl_pkey_get_public返回false,后续调用openssl_pkey_get_details自然会触发类型错误
解决步骤
1. 获取并解析Apple的JWK密钥集
先请求Apple的接口获取密钥集,解析出对应kid的JWK(通常需要和ID Token里的kid匹配):
$jwks = json_decode(file_get_contents('https://appleid.apple.com/auth/keys'), true); // 假设你已经拿到对应kid的JWK,比如取第一个 $jwk = $jwks['keys'][0];
2. 将JWK转换为PEM格式公钥
推荐使用成熟库简化转换,比如phpseclib(需通过Composer安装:composer require phpseclib/phpseclib):
use phpseclib3\Crypt\RSA; $rsa = RSA::loadJWK(json_encode($jwk)); $pem = $rsa->getPublicKey()->toString('PKCS8');
如果不想用第三方库,可以手动处理(需处理base64url解码、ASN.1组装):
// base64url转base64 function base64url_decode($data) { return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT)); } $n = base64url_decode($jwk['n']); $e = base64url_decode($jwk['e']); // 组装ASN.1结构 $modulus = pack('Ca*a*', 2, strlen($n), $n); $exponent = pack('Ca*a*', 2, strlen($e), $e); $rsa_public_key = pack('Ca*a*', 48, strlen($modulus.$exponent), $modulus.$exponent); // 转为PEM格式 $pem = '-----BEGIN PUBLIC KEY-----' . PHP_EOL; $pem .= chunk_split(base64_encode($rsa_public_key), 64, PHP_EOL); $pem .= '-----END PUBLIC KEY-----';
3. 正确加载并使用公钥
$res = openssl_pkey_get_public($pem); if ($res === false) { die('公钥加载失败:' . openssl_error_string()); } // 获取公钥详情 $details = openssl_pkey_get_details($res); print_r($details); // 后续可用于验证Apple ID Token等操作 // openssl_verify(...)
内容的提问来源于stack exchange,提问作者zzzdzdzd
相关产品推荐
相关产品推荐

