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(); } }
验证步骤
- 单独对比压缩结果:把TS和Java的压缩字节转成Base64字符串,看是否完全一致,一致说明压缩环节没问题。
- 对比最终的JWS字符串,或者直接用验证器测试,应该就能通过了。
内容的提问来源于stack exchange,提问作者Rajesh Rajendran
相关产品推荐
相关产品推荐

