728x90
새로운 프로젝트를 시작하면서 Spring Security와 JWT를 이용해 회원가입 및 로그인 기능을 구현하고 있다. JWT 생성 로직을 구현하고 테스트하는 과정에서 만난 간단한 에러에 대해 공유해 본다.
에러는 AccessToken과 RefreshToken이 잘 생성되는지 테스트하는 과정에서 발생했다.
The specified key byte array is 160 bits which is not secure enough for any JWT HMAC-SHA algorithm. The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a size >= 256 bits (the key size must be greater than or equal to the hash output size). Consider using the io.jsonwebtoken.security.Keys#secretKeyFor(SignatureAlgorithm) method to create a key guaranteed to be secure enough for your preferred HMAC-SHA algorithm. See <https://tools.ietf.org/html/rfc7518#section-3.2> for more information.
io.jsonwebtoken.security.WeakKeyException: The specified key byte array is 160 bits which is not secure enough for any JWT HMAC-SHA algorithm. The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a size >= 256 bits (the key size must be greater than or equal to the hash output size). Consider using the io.jsonwebtoken.security.Keys#secretKeyFor(SignatureAlgorithm) method to create a key guaranteed to be secure enough for your preferred HMAC-SHA algorithm. See <https://tools.ietf.org/html/rfc7518#section-3.2> for more information.
...
테스트 코드는 아래와 같다.
@Slf4j
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class JwtTokenizerTest {
private static JwtTokenizer jwtTokenizer;
private String secretKey;
private String base64EncodedSecretKey;
@BeforeAll
public void init() {
jwtTokenizer = new JwtTokenizer();
secretKey = "testSecretKey20230327";
base64EncodedSecretKey = jwtTokenizer.encodeBase64SecretKey(secretKey);
}
@DisplayName(value = "Secret Key 암호화")
@Test
void encodeBase64SecretKeyTest() throws Exception {
log.info(base64EncodedSecretKey);
assertThat(secretKey, is(new String(Decoders.BASE64.decode(base64EncodedSecretKey))));
}
// 테스트 실패 : 에러 발생!
@DisplayName(value = "Access Token 생성")
@Test
void genreateAccessTokenTest() throws Exception {
Map<String, Object> claims = new HashMap<>();
claims.put("memberId", 1);
claims.put("roles", List.of("USER"));
String subject = "access token";
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, 10);
Date expiration = calendar.getTime();
String accessToken = jwtTokenizer.generateAccessToken(claims, subject, expiration, base64EncodedSecretKey);
log.info("accessToken : " + accessToken);
assertThat(accessToken, notNullValue());
}
// 테스트 실패 : 에러 발생!
@DisplayName(value = "Refresh Token 생성")
@Test
void generateRefreshTokenTest() throws Exception {
String subject = "refresh token";
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR, 24);
Date expiration = calendar.getTime();
String refreshToken = jwtTokenizer.generateRefreshToken(subject, expiration, base64EncodedSecretKey);
log.info("refreshToken : " + refreshToken);
assertThat(refreshToken, notNullValue());
}
}
원인
원인은 에러 메세지에서 아주 자세히 알려주고 있다. JWT를 암호화할 때 안전하게 사용하기 위해서는 Secret Key가 256bit 이상어야 하는데 내가 테스트에 넣어놓은 Secret Key는 160bit밖에 되지 않아서 안전하지 않다는 것이다.
해결
Secret Key가 짧아서 발생한 문제라는걸 알았으니 secretKey를 256bit 이상으로 입력해주면 된다.
@BeforeAll
public void init() {
jwtTokenizer = new JwtTokenizer();
secretKey = "testSecretKey20230327"; // 기존
base64EncodedSecretKey = jwtTokenizer.encodeBase64SecretKey(secretKey);
}
@BeforeAll
public void init() {
jwtTokenizer = new JwtTokenizer();
secretKey = "testSecretKey20230327testSecretKey20230327testSecretKey20230327"; // 변경
base64EncodedSecretKey = jwtTokenizer.encodeBase64SecretKey(secretKey);
}
secretKey를 256bit 이상으로 수정했더니 모든 테스트가 잘 통과된다.
마무리
전에는 이런 사소한 에러는 따로 정리할 생각을 못했는데, 이번에 이력서와 포트폴리오를 준비하다보니 그 때 정리할걸…이라는 생각을 많이 했다. 이번 프로젝트에서는 새롭게 배우고 작업한 내용들에 대해 꼼꼼히 정리해보려고 한다. 이제는 에러를 만나면 포트폴리오에 넣을 이야기가 하나 더 생긴다는 생각에 기쁘다ㅎㅎ
728x90