不使用NimbusDS库实现Java JWT签名遇错误求助
问题:Java原生库实现JWT客户端断言生成失败排查
原NimbusDS实现代码
public static String getClientJwtAssertion(String secureKey, PrivateKey privateKey) { try { Date now = new Date(); SignedJWT signedJWT = new SignedJWT((new JWSHeader.Builder(JWSAlgorithm.RS256)).keyID(getThumbprint(secureKey)).build(), (new com.nimbusds.jwt.JWTClaimsSet.Builder()) .jwtID(UUID.randomUUID().toString()) .subject(Constants.CLIENT_ID) .issuer(Constants.CLIENT_ID) .issueTime(new Date()) .expirationTime(new Date(now.getTime() + Constants.EXPIRES_IN)) .build()); Set<JWSSignerOption> options = Collections.singleton(AllowWeakRSAKey.getInstance()); JWSSigner rsaSigner = new RSASSASigner(privateKey, options); signedJWT.sign(rsaSigner); return signedJWT.serialize(); } catch (Exception e) { return null; } }
自行编写的Java原生实现代码
public static String signJWTToken(Certificate certificate, PrivateKey privateKey) { String header = "{\"alg\":\"RS256\"}"; String claimTemplate = "'{'\"jti\": \"{0}\", \"sub\": \"{1}\", \"aud\": \"{2}\", \"iss\": \"{3}\", \"iat\": \"{4}\", \"exp\": \"{5}\"'}'"; StringBuilder token = new StringBuilder(); try { token.append(Base64.encodeBase64URLSafeString(header.getBytes(StandardCharsets.UTF_8))); token.append("."); Date now = new Date(); String[] claimArray = new String[6]; claimArray[0] = UUID.randomUUID().toString(); claimArray[1] = Constants.CLIENT_ID; claimArray[2] = Constants.ACCESS_TOKEN_URI; claimArray[3] = Constants.CLIENT_ID; claimArray[4] = Long.toString(now.getTime()); claimArray[5] = Long.toString(now.getTime() + Constants.EXPIRES_IN); MessageFormat claims= new MessageFormat(claimTemplate); // create payload and add to token String payload = claims.format(claimArray); token.append(Base64.encodeBase64URLSafeString(payload.getBytes(StandardCharsets.UTF_8))); // Sign JWT header Signature signature = Signature.getInstance("AllowWeakRSAKey"); signature.initSign(privateKey); signature.update(token.toString().getBytes(StandardCharsets.UTF_8)); String signedPayload = Base64.encodeBase64URLSafeString((signature.sign())); token.append("."); token.append(signedPayload); } catch (Exception e) { new Throw Exception(e); } return token.toString(); }
出现的错误信息
加密块大小异常
Caused by: javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes at java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:349) at java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:406) at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2088) at java.base/java.security.Signature$CipherAdapter.engineSign(Signature.java:1529) ... 4 common frames omitted
服务器认证错误
23:36:59.867 ERROR TokenApplication - Error retrieving access token org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: "{\"error\":\"invalid_client\",\"error_description\":\"MSIS9622: Client authentication failed. Please verify the credential provided for client authentication is valid.\"}" at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:101)
排查思路
- 签名算法完全错误:
Signature.getInstance("AllowWeakRSAKey")是无效的算法名,RS256对应的标准签名算法应为"SHA256withRSA"。错误的算法会导致底层加密逻辑混乱,直接触发块大小异常。 - JWT声明字段不匹配:原代码没有
aud字段,自行编写的代码新增了该字段并赋值为Constants.ACCESS_TOKEN_URI,需确认服务端是否要求该字段,或是否与原实现的断言结构一致,多余或不匹配的字段会导致认证失败。 - 时间戳格式错误:JWT的
iat和exp要求是UNIX时间戳(秒级),但代码中使用了now.getTime()(毫秒级),会导致时间戳数值过大,服务端会判定断言过期或无效,需改为now.getTime() / 1000。 - Payload生成语法错误:
claimTemplate中的'{'和'}'多余,会导致生成的JSON格式错误(出现单引号包裹大括号),正确模板应为"{\"jti\": \"{0}\", \"sub\": \"{1}\", \"aud\": \"{2}\", \"iss\": \"{3}\", \"iat\": \"{4}\", \"exp\": \"{5}\"}",JSON格式错误会让服务端无法解析断言。 - Base64编码合规性检查:需确认
Base64.encodeBase64URLSafeString是否符合JWT标准的Base64URL编码要求(自动去除填充字符=),编码不符合标准会导致服务端无法正确解码断言内容。 - 弱密钥处理缺失:原代码设置了
AllowWeakRSAKey选项,若使用的RSA密钥长度过短(如小于2048位),原生Signature默认会拒绝,需检查密钥长度;若必须使用弱密钥,需调整JVM安全策略或更换合规密钥。 - 异常处理语法错误:代码中
new Throw Exception(e)是无效语法,应改为throw new RuntimeException(e),否则异常被静默处理,无法定位中间环节的问题。
内容的提问来源于stack exchange,提问作者user583726
相关产品推荐
相关产品推荐

