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

Java与TypeScript生成智能健康卡JWS签名结果不一致问题

问题根源与解决方法

你遇到的核心问题是Java实现和TypeScript的Raw Deflate处理、JWS签名逻辑不一致,导致最终签名无法通过验证,主要有几个关键点:


1. 最关键的错误:签名数据不匹配

TypeScript里是直接对Raw Deflate后的二进制字节做JWS签名,但你的Java代码里,先把压缩后的字节做了Base64编码,然后用这个编码后的字符串去签名——相当于签名的是Base64文本的字节,和TS的签名原始数据完全不是一回事,这肯定过不了验证。

2. 字符编码不一致

TypeScript的pako.deflateRaw处理的是JSON字符串的UTF-8编码字节,但Java里jsonString.getBytes()默认用的是系统平台编码(比如Windows可能是GBK),这会导致压缩前的原始字节就有差异,压缩结果自然不一样。

3. Deflater的细节处理

虽然你设置了nowrap=true(对应Raw Deflate),但DeflaterOutputStream.finish()可能没有彻底排空所有压缩数据,导致输出的压缩字节不完整。


修正后的Java代码

首先修正Raw Deflate处理(返回原始压缩字节,确保编码一致)

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.nio.charset.StandardCharsets;

protected byte[] rawDeflate(String jsonString) throws IOException {
    // 强制用UTF-8编码,和TypeScript对齐
    byte[] jsonBytes = jsonString.getBytes(StandardCharsets.UTF_8);
    
    ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
    // nowrap=true 开启Raw Deflate模式,不添加zlib头和校验尾
    Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION, true);
    
    try (DeflaterOutputStream deflaterStream = new DeflaterOutputStream(bytesOut, deflater)) {
        deflaterStream.write(jsonBytes);
        deflaterStream.finish();
        
        // 确保Deflater剩余的所有压缩数据都写入输出流
        while (!deflater.finished()) {
            byte[] buf = new byte[1024];
            int len = deflater.deflate(buf);
            if (len > 0) {
                bytesOut.write(buf, 0, len);
            }
        }
    } finally {
        deflater.end(); // 释放Deflater资源,避免内存泄漏
    }
    
    return bytesOut.toByteArray();
}

然后修正JWS签名逻辑(直接用原始压缩字节签名)

private String signPayload(byte[] deflatedBytes, int index) {
    JwsHeaders jsHeaders = new JwsHeaders(SignatureAlgorithm.ES256);
    InputStream is = getClass().getClassLoader().getResourceAsStream("issuer.jwks.private.json");
    
    try {
        JsonWebKeys keySet = JwkUtils.readJwkSet(is);
        JsonWebKey key = keySet.getKeys().get(index);
        
        jsHeaders.setProperty("zip", "DEF");
        jsHeaders.setProperty("kid", key.getKeyId());
        
        // 直接传入压缩后的原始字节,而不是Base64字符串
        JwsCompactProducer jwsJsonProducer = new JwsCompactProducer(jsHeaders, deflatedBytes);
        String signed = jwsJsonProducer.signWith(key);
        
        return signed;
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    return "";
}

调用示例

public void generateHealthCard(String jsonPayload) {
    try {
        byte[] deflatedBytes = rawDeflate(jsonPayload);
        String signedJws = signPayload(deflatedBytes, 0);
        // 这里拿到的signedJws就是符合要求的签名结果,可以用来生成智能健康卡
    } catch (IOException e) {
        e.printStackTrace();
    }
}

验证步骤
  1. 单独对比压缩结果:把TS和Java的压缩字节转成Base64字符串,看是否完全一致,一致说明压缩环节没问题。
  2. 对比最终的JWS字符串,或者直接用验证器测试,应该就能通过了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 11:24:17