Spring JWT HS256 vs ES256

‍박태우·2024년 10월 15일

Spring

목록 보기
5/6
@Value("${jwt.secret.key}")
private String secretKey;
private Key key;
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.ES256;

 @PostConstruct
    public void init() {
        byte[] bytes = Base64.getDecoder().decode(secretKey);
        key = Keys.hmacShaKeyFor(bytes);
    }

// JWT 생성
public String createToken(String email, UserRoleEnum role) {
    Date date = new Date();

    return BEARER_PREFIX +
            Jwts.builder()
                    .setSubject(email)
                    .claim(AUTHORIZATION_KEY, role)
                    .setExpiration(new Date(date.getTime() + TOKEN_TIME))
                    .setIssuedAt(date)
                    .signWith(key, signatureAlgorithm)
                    .compact();
    }

위 jwt 토큰을 해당 알고지름을 이용하여 생성하고 프로그램을 실행해보니 로그인 마다 아래와 같은 오류가 발생하였다.

  • 아무리 봐도 생성도 잘 하고 있고 null 값 처리도 잘 해 주고 있는데 왜 토큰이 생성되지 않고 찾을 수 없다고 하는 건지 계속 코드를 보다가 아래 코드를 보았다.
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.ES256;
  • ES256 알고리즘이었는데 이 알고리즘을 HS256 알고리즘으로 변경S256 알고리즘으로 변경하니까 정상적으로 토큰이 생성되었다.

그럼 이 두 알고리즘의 차이는 무엇일지 검색해보았다.

ES256 vs HS256

HS256 (HMAC SHA-256)

  • 타입: 비대칭 키 방식이 아니라 대칭 키 방식

  • 서명 방식: 동일한 비밀 키를 사용하여 JWT의 서명과 검증을 모두 수행

  • 성능: 상대적으로 빠르며, 일반적으로 CPU 리소스를 적게 사용

  • 키 관리: 키가 유출되면 전체 시스템의 보안이 위협받을 수 있으므로, 비밀 키의 안전한 관리가 중요

  • 사용 예: 내부 서비스 간의 통신이나, 서버 간의 신뢰된 관계가 있는 경우에 적합합니다.

ES256 (ECDSA with SHA-256)

  • 타입: 비대칭 키 방식입니다. 즉, 공개 키와 개인 키 쌍을 사용

  • 서명 방식: 개인 키로 서명하고, 공개 키로 서명을 검증

  • 성능: 대칭 키 방식보다 상대적으로 느릴 수 있지만, 작은 키 사이즈로도 높은 보안성을 제공

  • 키 관리: 공개 키를 사용하여 서명을 검증하므로, 비밀 키가 유출되더라도 공개 키는 안전하게 배포할 수 있습니다. 이는 시스템 간의 신뢰 관계를 보다 확장 가능하게 만듬

  • 사용 예: 다수의 클라이언트와 서버가 존재하는 환경이나, 외부 서비스와의 통신 시 적합

그렇다면 아래 코드에서의 문제 이유는?

 @PostConstruct
    public void init() {
        byte[] bytes = Base64.getDecoder().decode(secretKey);
        key = Keys.hmacShaKeyFor(bytes);
    }



public String createToken(String email, UserRoleEnum role) {
    Date date = new Date();

    return BEARER_PREFIX +
            Jwts.builder()
                    .setSubject(email)
                    .claim(AUTHORIZATION_KEY, role)
                    .setExpiration(new Date(date.getTime() + TOKEN_TIME))
                    .setIssuedAt(date)
                    .signWith(key, signatureAlgorithm)
                    .compact();
    }

이유는 바로 위 PostConstruct 를 사용하여 key 를 초기화 할때 방식이 올바르지 않기 때문이다.

@PostConstruct
public void init() {
    // ECDSA 개인 키를 생성합니다.
    try {
        // ECDSA 키 생성 (여기서는 예시로 변형 가능)
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
        keyGen.initialize(256);
        KeyPair keyPair = keyGen.generateKeyPair();
        privateKey = keyPair.getPrivate(); // 개인 키
        publicKey = keyPair.getPublic(); // 공개 키
    } catch (NoSuchAlgorithmException e) {
        log.error("키 생성 실패", e);
    }
}

이 방식이 ES 방식 즉 ECDSA 키를 이용하여 개인 키, 공개 키 를 구별하여 생성하는 방식이다.

=> 해당 방식이 로그인 시에는 어떤식으로 쓰일 지는 잘 모르겠지만 한 번 알아두고 나중에 이런 방식도 있구나 생각해 보아야 겠다.

profile
잘 부탁드립니다.

0개의 댓글