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

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";
        }
    }
}

关键修正点说明

  1. 替换序列化器:将CompactSerializer改为JsonGeneralSerializer,该格式支持存储多接收方的JWE结构,序列化后会包含完整的recipients数组。
  2. 添加密钥标识kid:为每个密钥对添加唯一kid字段,解密时通过该标识匹配对应接收方,避免依赖固定索引导致的错误。
  3. 动态匹配接收方:解密时遍历所有接收方,找到与当前私钥kid一致的索引,再执行解密操作,确保每个私钥对应正确的接收方数据。
  4. 错误提示优化:增加接收方匹配失败的提示,便于快速排查问题。

内容的提问来源于stack exchange,提问作者gooddev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 02:50:17