使用公钥解码验证JWS Token 读取外部密钥文件出现签名不匹配问题
问题原因与解决方案
1 最高发诱因:Base64编解码格式不统一
导出密钥和读取密钥时用的Base64处理逻辑不一致,是该问题的最常见原因:
- 若导出时使用
Base64.getMimeEncoder(),会自动在每76个字符后插入换行符,哪怕你看到的是单行内容,也可能存在不可见的空白字符 - 若导出时使用
Base64.getUrlEncoder(),会将+替换为-、/替换为_、删除末尾的补位=,但你解码时用的是默认Base64.getDecoder(),无法识别URL安全格式的Base64,最终解码出的密钥内容错误,自然无法匹配签名。
解决方案
统一编解码逻辑,读取密钥时先清洗异常字符再解码:
public PrivateKey getPrivateKey() throws Exception { String base64PrivateKey = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource("private.key").toURI()))); // 清洗所有空白字符、UTF-8 BOM头 base64PrivateKey = base64PrivateKey.replaceAll("\\s+", "").replace("\uFEFF", ""); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec (Base64.getDecoder().decode(base64PrivateKey.getBytes())); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(keySpec); } public PublicKey getPublicKey() throws Exception { String base64PublicKey = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource("public.key").toURI()))); // 清洗所有空白字符、UTF-8 BOM头 base64PublicKey = base64PublicKey.replaceAll("\\s+", "").replace("\uFEFF", ""); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes())); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(keySpec); }
导出密钥时统一使用无换行的普通Base64实现:
// 公钥导出示例,私钥导出逻辑同理 out = new FileWriter("public.key"); out.write(Base64.getEncoder().encodeToString(kp.getPublic().getEncoded())); out.close();
2 次要排查方向:密钥文件内容错误
- 确认导出公私钥时没有写反文件,不要把私钥内容存入
public.key、公钥内容存入private.key - 确认后续替换密钥文件时,公私钥文件对应正确,没有放错位置。可以将动态生成的密钥
getEncoded()后的Base64值,和文件读取到的内容做对比,完全一致才符合预期。
3 补充排查:签名算法一致性
确认用重构后的私钥生成Token时,签名算法仍然为RS256,没有误修改为其他算法。
内容的提问来源于stack exchange,提问作者Stijn Geukens
相关产品推荐
相关产品推荐

