Welcome! Everything is fine.

[Spring] JWT 시크릿 키 설정 및 문제 해결 / -hex 64 vs -base64 32 차이 본문

TIL

[Spring] JWT 시크릿 키 설정 및 문제 해결 / -hex 64 vs -base64 32 차이

개발곰발 2025. 2. 27. 19:23
728x90

Main 실행 시 에러 발생

처음 프로젝트를 받아 실행하니 다음과 같은 에러가 나왔다.

 

에러 메세지가 길긴 하지만 잘 읽어보면 JWT 시크릿 키에 대한 문제인 것을 짐작할 수 있다. application.properties 파일에 키를 넣으려고 했는데, 아예 resourse 폴더가 없었다. 따라서 먼저 키를 만들어야 했다.

java.lang.IllegalArgumentException: Could not resolve placeholder 'jwt.secret.key' in value "${jwt.secret.key}"
org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'jwtUtil': Injection of autowired dependencies failed
Unsatisfied dependency expressed through constructor parameter 0: 
Error creating bean with name 'jwtUtil': Injection of autowired dependencies failed

 

터미널에 아래와 같이 입력해 키를 만들었다. 키를 처음 만들어 보지만 검색하니 만드는 방법이 바로 나왔다. 터미널로 만드는 방법 외에도 키를 만드는 사이트를 활용하는 방법도 있었다.

openssl rand -hex 64

 

결과는 다음과 같이 아주 긴 시크릿 키가 나왔다.

22b8c9ae9a1b397ef0c8a2d142f91d1d00b7c0dc0dae01d2999d197f111f3e01c15a4477f63732fa2aa6fe8fbf313874b681fa31cef4049aa50707b7f8156

 

그리고 resourse 폴더를 생성 후, application.properties 파일을 만들고 다음과 같이 키를 넣었다.

jwt.secret.key="JWT_시크릿_키"

 

여기서 중요한 점은 JwtUtil 클래스에서 ${jwt.secret.key} 로 적었다면, jwt.secret.key 로 똑같이 적어야 한다는 점이다.

 

그런데 또 에러가 났다. 왜일까? 잘 살펴보니 새로운 에러가 등장했다.

java.lang.IllegalArgumentException: Illegal base64 character 22

 

해당 에러에 대한 내용을 검색하다가 JWT를 사용한 다른 사람의 스크린샷을 보고 나의 실수를 알아차렸다. 바로 쌍따옴표로 키를 감싼 것이다.

 

이렇게 작성하지 말고,

jwt.secret.key="JWT_시크릿_키"

 

이렇게 쌍따옴표 없이 작성해야 한다. 제대로 고친 후 실행하니 정상적으로 작동했다.

jwt.secret.key=JWT_시크릿_키

 

+) 추가 공부 👀

처음에는 시크릿 키 값을 바꾼 것 때문에 정상 작동하는 줄 알았지만 아니었다. 키 값이 문제는 아니었지만, 내가 위에서 만든 키 값은 현재 프로젝트에서 사용하고 있는 알고리즘에 적합하지 않다는 것을 알게 되었다.

 

현재 프로젝트에서 적용한 알고리즘은 HS256 알고리즘이다.

private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

 

사용하는 알고리즘에 따라 키를 다르게 생성해야 한다. 내가 위에서 키를 생성하기 위해 사용한 openssl rand -hex 64 는 HS512(512비트) 알고리즘을 사용할 때 적합하다.

 

따라서 다음과 같이 조금 다른 명령어를 gitbash에 치니 더 짧은 키가 나왔다.

openssl rand -base64 32

 

결과를 보니 확실히 위에서 얻은 키와 다르다.

7yD9Da2chXAX5qCI2WrIzdiIifDZI8WPtcqJ+rjUJRA=

 

💡-hex 64 vs -base64 32 차이

1) -hex 64 (HEX)

  • HS512(512비트) 알고리즘을 사용할 때 적합
  • 다만, HEX 문자열은 Base64와 다르게 바로 Secret Key로 사용하려면 바이트 변환이 필요함
  • 예제 변환 코드
    byte[] keyBytes = new BigInteger(secretKey, 16).toByteArray();

 

2) -base64 32 (Base64)

  • HS256(256비트) 알고리즘을 사용할 때 적합
  • Base64.getDecoder().decode(secretKey)를 통해 바로 바이트 배열로 변환 가능
  • Keys.hmacShaKeyFor(Base64.getDecoder().decode(secretKey))와 같은 방식으로 사용 가능

그런데, openssl rand -hex 64 를 이용해 생성한 키도 정상적으로 돌아가서 의문이 들었다. HS256 알고리즘에 적합한 키는 openssl rand -base64 32 로 생성한 키라고 했는데, 왜 정상적으로 작동하는 걸까? 이 부분은 GPT에게 물어봤다.🤔

 

그 이유는 Spring Security 및 JJWT 라이브러리에서 내부적으로 HEX 문자열을 바이트 배열로 변환하여 처리하기 때문이라고 한다.

  • JJWT (Java JWT, JSON Web Token for Java) : Java 기반의 JWT(Json Web Token) 생성 및 검증을 위한 라이브러리로, JWT를 쉽게 다룰 수 있도록 도와준다.

JJWT에서 Keys.hmacShaKeyFor() 메서드는 다음과 같이 동작한다. byte[] keyBytes를 입력 받아 SecretKey 객체로 변환하는데, 여기서 입력값이 256비트보다 길면 자동으로 적절한 길이로 잘라서 사용한다.

public static SecretKey hmacShaKeyFor(byte[] keyBytes) {
    return new SecretKeySpec(keyBytes, SignatureAlgorithm.HS256.getJcaName());
}

 

따라서, 키가 길어도 문제가 발생하지 않는다. 하지만 불필요하게 긴 값을 사용하기보단 Base64 키를 사용해 변환 과정을 없애는 것이 더 효율적일 것이다.