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

如何使用JOSE4J解密JWE?前后端加解密遇算法密钥适配问题

问题分析与解决方案

核心问题

你当前的问题根源是混淆了对称加密与非对称加密的逻辑:

  • 前端使用dir算法(直接对称加密)配合A128CBC-HS256套件,本质是对称加密——前后端共用同一个共享密钥完成加密解密。
  • 后端错误地将这个共享密钥当作RSA公钥处理,用KeyFactory.getInstance("RSA")解析自然会失败,因为该密钥并非RSA公钥格式。
  • HS256是HMAC签名算法,和RSA无关联;且你用的是JWE(加密型JWT),但后端的JwtConsumer默认只处理JWS(签名型JWT),不支持JWE解密。

解决方案一:继续使用对称加密(共享密钥场景)

若你的业务场景适合前后端安全共享同一密钥,可按以下方式修改:

前端代码(保持不变,明确为对称密钥)

const secret = jose.base64url.decode('zH4NRP1HMALxxCFnRZABFA7GOJtzUAgIj02alfL1lvI');
const jwt = await new jose.EncryptJWT({ foo: 'bar' })
  .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256' })
  .setIssuedAt()
  .setIssuer('urn:example:issuer')
  .setAudience('urn:example:audience')
  .setExpirationTime('2h')
  .encrypt(secret);

后端代码(改用Nimbus JOSE+JWT处理JWE解密)

先引入Nimbus依赖(Maven示例):

<dependency>
    <groupId>com.nimbusds</groupId>
    <artifactId>nimbus-jose-jwt</artifactId>
    <version>9.37.3</version> <!-- 使用最新稳定版 -->
</dependency>

修改解密逻辑:

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.DirectDecrypter;
import com.nimbusds.jwt.EncryptedJWT;

import java.text.ParseException;
import java.util.Base64;

public class JweSymmetricDecrypt {
    public static void main(String[] args) throws ParseException, JOSEException {
        String encryptedJwt = "替换为前端传入的JWT字符串";
        byte[] secretKey = Base64.getUrlDecoder().decode("zH4NRP1HMALxxCFnRZABFA7GOJtzUAgIj02alfL1lvI");

        // 解析加密JWT
        EncryptedJWT jwt = EncryptedJWT.parse(encryptedJwt);
        // 对称密钥解密
        JWEDecrypter decrypter = new DirectDecrypter(secretKey);
        jwt.decrypt(decrypter);

        // 获取声明内容
        System.out.println(jwt.getJWTClaimsSet().getStringClaim("foo")); // 输出 bar
        System.out.println(jwt.getJWTClaimsSet().getIssuer());
        System.out.println(jwt.getJWTClaimsSet().getAudience());
    }
}

解决方案二:改用非对称加密(公钥加密+私钥解密场景)

若需要避免共享密钥的风险,可使用RSA密钥对实现非对称加密:

第一步:生成RSA密钥对

用Java代码生成(或通过OpenSSL工具生成):

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class RsaKeyGenerator {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(2048); // 推荐2048位及以上长度
        KeyPair keyPair = keyGen.generateKeyPair();

        // 公钥(提供给前端)
        String publicKeyBase64 = Base64.getUrlEncoder().encodeToString(keyPair.getPublic().getEncoded());
        // 私钥(后端安全存储)
        String privateKeyBase64 = Base64.getUrlEncoder().encodeToString(keyPair.getPrivate().getEncoded());

        System.out.println("公钥: " + publicKeyBase64);
        System.out.println("私钥: " + privateKeyBase64);
    }
}

前端代码(用RSA公钥加密JWT)

import * as jose from 'jose';

// 替换为生成的RSA公钥(PEM格式或base64编码)
const publicKey = await jose.importSPKI('你的RSA公钥内容', 'RSA-OAEP-256');

const jwt = await new jose.EncryptJWT({ foo: 'bar' })
  .setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A128CBC-HS256' })
  .setIssuedAt()
  .setIssuer('urn:example:issuer')
  .setAudience('urn:example:audience')
  .setExpirationTime('2h')
  .encrypt(publicKey);

后端代码(用RSA私钥解密)

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jwt.EncryptedJWT;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.ParseException;
import java.util.Base64;

public class RsaJweDecrypt {
    public static void main(String[] args) throws Exception {
        String encryptedJwt = "前端传入的加密JWT";
        String privateKeyBase64 = "你的RSA私钥base64内容";

        // 解析私钥
        byte[] privateKeyBytes = Base64.getUrlDecoder().decode(privateKeyBase64);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        // 解密JWE
        EncryptedJWT jwt = EncryptedJWT.parse(encryptedJwt);
        JWEDecrypter decrypter = new RSADecrypter(privateKey);
        jwt.decrypt(decrypter);

        // 获取声明内容
        System.out.println(jwt.getJWTClaimsSet().getStringClaim("foo"));
    }
}

关键注意事项

  • dir算法为对称加密专用,只能用同一共享密钥解密,无法适配RSA密钥对。
  • 非对称加密场景下,前端仅能用公钥加密,后端用对应私钥解密,不可反向操作。
  • 处理JWE时需使用支持JWE的库,原JwtConsumer(多来自JJWT)仅支持JWS,推荐使用Nimbus JOSE+JWT。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:56:04