PHP JWT框架JWE多接收方解密失败问题求助
多接收方JWE加密解密的正确实现方案
问题根源
你遇到的核心问题是使用了不支持多接收方的Compact序列化格式。JWE的Compact Serializer仅能处理单接收方场景,当添加多个接收方时,后续接收方的信息会被直接丢弃,导致第二个私钥无法找到对应的接收方加密数据,最终解密失败。
修正方案
要实现多接收方JWE,必须使用JSON通用序列化器(JsonGeneralSerializer),同时解密时需要通过密钥标识kid匹配对应接收方,而非固定使用索引0。
修正后的完整代码
require __DIR__ . '/../../vendor/autoload.php'; use Jose\Component\Core\AlgorithmManager; use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512; use Jose\Component\Encryption\Algorithm\KeyEncryption\RSAOAEP256; use Jose\Component\Encryption\Compression\CompressionMethodManager; use Jose\Component\Encryption\Compression\Deflate; use Jose\Component\Encryption\JWEBuilder; use Jose\Component\Encryption\Serializer\JsonGeneralSerializer; // 替换为支持多接收方的JSON序列化器 use Jose\Component\KeyManagement\JWKFactory; use Jose\Component\Encryption\JWEDecrypter; use Jose\Component\Core\JWK; class Multiplereceipient_1 extends CI_Controller { private $private_key1; private $private_key2; private $public_key1; private $public_key2; public function __construct() { $this->private_key1 = ""; $this->private_key2 = ""; $this->public_key1 = ""; $this->public_key2 = ""; } public function index() { $payload = json_encode([ 'iat' => time(), 'nbf' => time(), 'exp' => time() + 3600, 'iss' => 'My service', 'aud' => 'Your application', ]); $this->encrypt($payload); } public function encrypt($payload) { // 初始化算法管理器 $keyEncryptionAlgorithmManager = new AlgorithmManager([ new RSAOAEP256() ]); $contentEncryptionAlgorithmManager = new AlgorithmManager([ new A256CBCHS512(), ]); $compressionMethodManager = new CompressionMethodManager([ new Deflate(), ]); // 创建JWE构建器 $jweBuilder = new JWEBuilder( $keyEncryptionAlgorithmManager, $contentEncryptionAlgorithmManager, $compressionMethodManager ); // 生成带唯一标识kid的密钥对,方便解密时匹配接收方 $this->private_key1 = JWKFactory::createRSAKey( 2048, [ 'alg' => 'RSA-OAEP-256', 'use' => 'enc', 'kid' => 'recipient1-unique-key' ] ); $this->private_key2 = JWKFactory::createRSAKey( 2048, [ 'alg' => 'RSA-OAEP-256', 'use' => 'enc', 'kid' => 'recipient2-unique-key' ] ); $this->public_key1 = $this->private_key1->toPublic(); $this->public_key2 = $this->private_key2->toPublic(); // 构建多接收方JWE $jwe = $jweBuilder ->create() ->withPayload($payload) ->withSharedProtectedHeader([ 'alg' => 'RSA-OAEP-256', 'enc' => 'A256CBC-HS512', 'zip' => 'DEF' ]) ->addRecipient($this->public_key1) ->addRecipient($this->public_key2) ->build(); // 使用JSON通用序列化器序列化(支持多接收方结构) $serializer = new JsonGeneralSerializer(); $serializedJwe = $serializer->serialize($jwe); echo "<pre>序列化后的多接收方JWE:\n"; echo $serializedJwe; echo "</pre>"; // 测试两个私钥解密 echo "<h3>接收方1解密结果:</h3>"; $this->decrypt($serializedJwe, $this->private_key1); echo "<h3>接收方2解密结果:</h3>"; $this->decrypt($serializedJwe, $this->private_key2); } public function decrypt($serializedJwe, JWK $privateKey) { // 反序列化JWE $serializer = new JsonGeneralSerializer(); $jwe = $serializer->unserialize($serializedJwe); // 初始化解密所需管理器 $keyEncryptionAlgorithmManager = new AlgorithmManager([ new RSAOAEP256() ]); $contentEncryptionAlgorithmManager = new AlgorithmManager([ new A256CBCHS512(), ]); $compressionMethodManager = new CompressionMethodManager([ new Deflate(), ]); $decrypter = new JWEDecrypter( $keyEncryptionAlgorithmManager, $contentEncryptionAlgorithmManager, $compressionMethodManager ); // 通过kid匹配对应的接收方索引 $recipientIndex = null; foreach ($jwe->getRecipients() as $index => $recipient) { if ($recipient->getHeader('kid') === $privateKey->get('kid')) { $recipientIndex = $index; break; } } if ($recipientIndex === null) { echo "未找到匹配当前私钥的接收方\n"; return; } // 使用匹配的接收方索引执行解密 if ($decrypter->decryptUsingKey($jwe, $privateKey, $recipientIndex)) { $decryptedPayload = $jwe->getPayload(); echo "<pre>"; echo $decryptedPayload; echo "</pre>"; } else { echo "解密失败\n"; } } }
关键修正点说明
- 替换序列化器:将
CompactSerializer改为JsonGeneralSerializer,该格式支持存储多接收方的JWE结构,序列化后会包含完整的recipients数组。 - 添加密钥标识kid:为每个密钥对添加唯一
kid字段,解密时通过该标识匹配对应接收方,避免依赖固定索引导致的错误。 - 动态匹配接收方:解密时遍历所有接收方,找到与当前私钥
kid一致的索引,再执行解密操作,确保每个私钥对应正确的接收方数据。 - 错误提示优化:增加接收方匹配失败的提示,便于快速排查问题。
内容的提问来源于stack exchange,提问作者gooddev
相关产品推荐
相关产品推荐

