如何修改PHP JWE代码适配A256CBC-HS512与RSA-OAEP-256算法
PHP JWE 实现:切换至 A256CBC-HS512/RSA-OAEP-256
上周对接某服务商新API,要求在HTTPS请求头中使用JWE进行身份认证。此前无JWE开发经验,查阅资料后用PHP实现,经测试调试得到以下可用代码。现需将加密算法enc改为A256CBC-HS512,密钥加密算法alg改为RSA-OAEP-256,以下是修改方案:
原可用代码
require_once("auth/key.php"); include 'vendor/autoload.php'; use phpseclib3\Crypt\PublicKeyLoader; function jwe (){ global $payloadAuth; //此处填入所需的payload /**************************************************/ /********************HEADER************************/ /**************************************************/ // 对header json进行base64编码 $arr = array('enc' => 'A256GCM', 'alg' => 'RSA-OAEP'); $arr2 = json_encode($arr); $encoded_header=base64url_encode($arr2); /**************************************************/ /********************JWE KEY***********************/ /**************************************************/ $CEK=openssl_random_pseudo_bytes(32); $encoded_EncCEK = base64url_encode(rsaEncryptionOaepSha256($publicKey, $CEK)); /**************************************************/ /********************VECTOR************************/ /**************************************************/ $iv = openssl_random_pseudo_bytes(12); $encoded_iv = base64url_encode($iv); /**************************************************/ /********************CYPHERTEXT********************/ $cipher = "aes-256-gcm"; $option=1; $aad=$encoded_header; $ciphertext=openssl_encrypt($payloadAuth, $cipher, $CEK, $option, $iv, $tag, $aad); $encoded_ciphertext=base64url_encode($ciphertext); /**************************************************/ /********************TAG***************************/ /**************************************************/ $encoded_tag=base64url_encode($tag); /**************************************************/ /********************FIN***************************/ /**************************************************/ return $encoded_header . '.' . $encoded_EncCEK . '.' . $encoded_iv . '.' . $encoded_ciphertext . '.' . $encoded_tag; } function base64url_encode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } function rsaEncryptionOaepSha256($publicKey, $plaintext) { global $publicKey; $rsa = PublicKeyLoader::load($publicKey)->withHash('sha1')->withMGFHash('sha1'); return $rsa->encrypt($plaintext); } ?>
修改方案与完整代码
要切换到A256CBC-HS512和RSA-OAEP-256,需要修改以下核心点:
关键修改说明
- Header更新:修改
alg为RSA-OAEP-256,enc为A256CBC-HS512 - CEK生成:
A256CBC-HS512需要64字节的复合密钥(前32字节用于AES-256-CBC加密,后32字节用于HMAC-SHA512签名) - RSA加密配置:
RSA-OAEP-256要求使用SHA-256哈希及MGF1-SHA256掩码生成函数 - 加密逻辑调整:改用AES-256-CBC加密,手动处理PKCS#7填充,IV长度改为16字节,同时基于AAD、IV、密文生成HMAC标签
修改后的完整代码
require_once("auth/key.php"); include 'vendor/autoload.php'; use phpseclib3\Crypt\PublicKeyLoader; function jwe (){ global $payloadAuth; // 填入所需的payload global $publicKey; // 从auth/key.php引入的公钥 /**************************************************/ /********************HEADER************************/ /**************************************************/ // 更新为目标算法标识 $arr = array('enc' => 'A256CBC-HS512', 'alg' => 'RSA-OAEP-256'); $arr2 = json_encode($arr); $encoded_header=base64url_encode($arr2); /**************************************************/ /********************JWE KEY***********************/ /**************************************************/ // A256CBC-HS512需要64字节复合密钥:前32字节AES密钥,后32字节HMAC密钥 $CEK=openssl_random_pseudo_bytes(64); $encoded_EncCEK = base64url_encode(rsaEncryptionOaep256($publicKey, $CEK)); /**************************************************/ /********************VECTOR************************/ /**************************************************/ // AES-CBC要求IV长度等于块大小(16字节) $iv = openssl_random_pseudo_bytes(16); $encoded_iv = base64url_encode($iv); /**************************************************/ /********************CYPHERTEXT********************/ $cipher = "aes-256-cbc"; // 配置为返回原始数据,手动处理填充 $option = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING; $aad = $encoded_header; // 对payload添加PKCS#7填充,确保长度为16字节的倍数 $blockSize = openssl_cipher_iv_length($cipher); $padding = $blockSize - (strlen($payloadAuth) % $blockSize); $payloadPadded = $payloadAuth . str_repeat(chr($padding), $padding); // 使用CEK前32字节进行AES-256-CBC加密 $ciphertext = openssl_encrypt($payloadPadded, $cipher, substr($CEK, 0, 32), $option, $iv); $encoded_ciphertext = base64url_encode($ciphertext); /**************************************************/ /********************TAG***************************/ /**************************************************/ // 生成HMAC-SHA512标签:AAD、IV、密文的base64url编码拼接后计算 $hmacInput = $aad . '.' . $encoded_iv . '.' . $encoded_ciphertext; // 使用CEK后32字节作为HMAC密钥 $tag = hash_hmac('sha512', $hmacInput, substr($CEK, 32), true); $encoded_tag = base64url_encode($tag); /**************************************************/ /********************FIN***************************/ /**************************************************/ return $encoded_header . '.' . $encoded_EncCEK . '.' . $encoded_iv . '.' . $encoded_ciphertext . '.' . $encoded_tag; } function base64url_encode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } function rsaEncryptionOaep256($publicKey, $plaintext) { // RSA-OAEP-256对应SHA-256哈希和MGF1-SHA256掩码函数 $rsa = PublicKeyLoader::load($publicKey)->withHash('sha256')->withMGFHash('sha256'); return $rsa->encrypt($plaintext); } ?>
额外说明
- PKCS#7填充:AES-CBC要求明文长度必须是块大小(16字节)的整数倍,因此手动实现了标准的PKCS#7填充逻辑
- HMAC计算逻辑:
A256CBC-HS512的标签由AAD、IV、密文的base64url编码拼接后,用CEK的后32字节计算HMAC-SHA512得到 - CEK拆分规则:64字节的CEK严格分为两部分,分别用于加密和签名,完全符合JWE对该算法的规范
内容的提问来源于stack exchange,提问作者lmontiel
相关产品推荐
相关产品推荐

