gpt4 book ai didi

java - 使用 Spring OAuth2 资源服务器和对称 key 时如何避免 KeyLengthException

转载 作者:行者123 更新时间:2023-12-05 02:37:59 25 4
gpt4 key购买 nike

所以我正在开发一个资源服务器(一个 Spring Boot 应用程序),我想利用 Spring Security OAuth2 资源服务器库的优点。

我现在面临的问题是授权服务器(另一个 Spring Boot 应用程序)使用对称 key 对 JWT 进行签名,很久以前它被设置为一个非常短的字符串,而我无法更改。

我按照 Spring Security 文档尝试了这个:

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().permitAll())
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
}

@Bean
public JwtDecoder jwtDecoder(@Value("${jwt.secret-key}") String secretKey) {
return NimbusJwtDecoder
.withSecretKey(new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HS512"))
.macAlgorithm(MacAlgorithm.HS512)
.build();
}
}

但是我得到以下错误:

Caused by: com.nimbusds.jose.KeyLengthException: The secret length must be at least 256 bits
at com.nimbusds.jose.crypto.impl.MACProvider.<init>(MACProvider.java:118) ~[nimbus-jose-jwt-9.10.1.jar:9.10.1]
at com.nimbusds.jose.crypto.MACVerifier.<init>(MACVerifier.java:168) ~[nimbus-jose-jwt-9.10.1.jar:9.10.1]
at com.nimbusds.jose.crypto.MACVerifier.<init>(MACVerifier.java:81) ~[nimbus-jose-jwt-9.10.1.jar:9.10.1]
at com.nimbusds.jose.crypto.MACVerifier.<init>(MACVerifier.java:113) ~[nimbus-jose-jwt-9.10.1.jar:9.10.1]
at com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory.createJWSVerifier(DefaultJWSVerifierFactory.java:100) ~[nimbus-jose-jwt-9.10.1.jar:9.10.1]
at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:364) ~[nimbus-jose-jwt-9.10.1.jar:9.10.1]
at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:303) ~[nimbus-jose-jwt-9.10.1.jar:9.10.1]
at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.createJwt(NimbusJwtDecoder.java:154) ~[spring-security-oauth2-jose-5.5.3.jar:5.5.3]
... 61 common frames omitted

据我所知,生成此异常的 MACProvider 不可配置,并且所需的 key 长度不能放宽。有什么办法解决这个问题吗?

谢谢

编辑:

尝试了用 0 填充键的建议,如下所示:

@Bean
public JwtDecoder jwtDecoder(@Value("${jwt.secret-key}") String secretKey) {
var key = secretKey.getBytes(StandardCharsets.UTF_8);
var paddedKey = Arrays.copyOf(key, 128);
return NimbusJwtDecoder
.withSecretKey(new SecretKeySpec(paddedKey, "HS512"))
.macAlgorithm(MacAlgorithm.HS512)
.build();
}

但现在我得到以下异常:

com.nimbusds.jose.proc.BadJWSException: Signed JWT rejected: Invalid signature
at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:378) ~[nimbus-jose-jwt-9.10.1.jar:9.10.1]
at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:303) ~[nimbus-jose-jwt-9.10.1.jar:9.10.1]
at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.createJwt(NimbusJwtDecoder.java:154) ~[spring-security-oauth2-jose-5.5.3.jar:5.5.3]
at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.decode(NimbusJwtDecoder.java:137) ~[spring-security-oauth2-jose-5.5.3.jar:5.5.3]
at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.getJwt(JwtAuthenticationProvider.java:97) ~[spring-security-oauth2-resource-server-5.5.3.jar:5.5.3]
at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.authenticate(JwtAuthenticationProvider.java:88) ~[spring-security-oauth2-resource-server-5.5.3.jar:5.5.3]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182) ~[spring-security-core-5.5.3.jar:5.5.3]
at org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:130) ~[spring-security-oauth2-resource-server-5.5.3.jar:5.5.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:178) ~[spring-security-oauth2-client-5.5.3.jar:5.5.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183) ~[spring-security-web-5.5.3.jar:5.5.3]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.3.12.jar:5.3.12]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.3.12.jar:5.3.12]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.12.jar:5.3.12]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.12.jar:5.3.12]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:96) ~[spring-boot-actuator-2.5.6.jar:2.5.6]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.12.jar:5.3.12]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

编辑2:

源代码像这样生成 JWT(使用 io.jsonwebtoken 0.9.1 库):

private String generateToken(Map<String, Object> claims, String username) {

Header header = Jwts.header();
header.setType("JWT");

String jti = UUID.randomUUID().toString();
Date now = new Date(System.currentTimeMillis());

return Jwts.builder()
.setClaims(claims)
.setHeader((Map<String, Object>) header)
.setSubject(username)
.setIssuedAt(now)
.setIssuer("issuer")
.setId(jti)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}

编辑3:

解决方法:

@Bean
public JwtDecoder jwtDecoder(@Value("${jwt.secret-key}") String secretKey) {
var key = TextCodec.BASE64.decode(secretKey);
var paddedKey = key.length < 128 ? Arrays.copyOf(key, 128) : key;
return NimbusJwtDecoder
.withSecretKey(new SecretKeySpec(paddedKey, "HS512"))
.macAlgorithm(MacAlgorithm.HS512)
.build();
}

最佳答案

看起来像,HMAC ,如果 secret 长度短于哈希算法的 block 大小,它将用零填充 secret 。

并根据this

Block size: the size of the data block the underlying hash algorithmoperates upon. For SHA-256, this is 512 bits, for SHA-384 and SHA-512,this is 1024 bits.

Output length: the size of the hash value produced by the underlyinghash algorithm. For SHA-256, this is 256 bits, for SHA-384 this is 384bits, and for SHA-512, this is 512 bits.

SHA-512 的 block 大小为 128 字节。

我建议,如果源使用 HS512 算法,请尝试用零填充 secret 以查看它是否有效。如果你的类路径中有 Guava 库:

Bytes.ensureCapacity(secretKey.getBytes(StandardCharsets.UTF_8), 128, 0);

ensureCapacity方法源码:

  public static byte[] ensureCapacity(byte[] array, int minLength, int padding) {
Preconditions.checkArgument(minLength >= 0, "Invalid minLength: %s", minLength);
Preconditions.checkArgument(padding >= 0, "Invalid padding: %s", padding);
return array.length < minLength ? Arrays.copyOf(array, minLength + padding) : array;
}

编辑 2:

首先尝试将 secret 解码为 base 64

byte[] decodedBytes = Base64.decodeBase64(secret)

然后添加填充并在解码器中使用它(如果 decodedBytes 的大小小于 128):

Arrays.copyOf(decodedBytes, 128);

我做了以下测试,一切正常:

  private String generateToken(Map<String, Object> claims, String username) {

Header header = Jwts.header();
header.setType("JWT");

String jti = UUID.randomUUID().toString();
Date now = new Date(System.currentTimeMillis());

return Jwts.builder()
.setClaims(claims)
.setHeader((Map<String, Object>) header)
.setSubject(username)
.setIssuedAt(now)
.setIssuer("issuer")
.setId(jti)
.signWith(SignatureAlgorithm.HS512, "asdf")
.compact();
}

public JwtDecoder jwtDecoder() {
// base64 decoder from org.apache.tomcat.util.codec.binary.Base64;
byte[] key = Base64.decodeBase64("asdf");
// var key = "asdf".getBytes(StandardCharsets.UTF_8);
var paddedKey = Arrays.copyOf(key, 128);
return NimbusJwtDecoder
.withSecretKey(new SecretKeySpec(paddedKey, "HS512"))
.macAlgorithm(MacAlgorithm.HS512)
.build();
}

并像这样使用它:

Main s = new Main();
String token = s.generateToken(new HashMap<>(), "hatef");
JwtDecoder decoder = s.jwtDecoder();
System.out.println(decoder.decode(token));

编辑 3:

OP 在评论中报告:

it worked withio.jsonwebtoken.impl.TextCodec.BASE64.decode(secretKey) instead oforg.apache.tomcat.util.codec.binary.Base64.decodeBase64(secretKey),with the latter i still had a com.nimbusds.jose.proc.BadJWSException

关于java - 使用 Spring OAuth2 资源服务器和对称 key 时如何避免 KeyLengthException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69829603/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com