JWT에 대해 사용해보게 되었는데 JWT에서 시그니처 부분을 암호화하기 위한 방식으로 HMAC 암호화를 사용하였습니다. 그래서 정의 및 예제코드를 작성하여 정리하였습니다.
HMAC이란
HMAC(Hash-based Message Authentication Code)은
메시지의 **무결성(Integrity)**과 **인증(Authentication)**을 보장하기 위한 암호화 방식
•무결성: 메시지가 중간에 변조되지 않았음을 확인
•인증: 메시지가 신뢰할 수 있는 주체로부터 왔음을 확인
동작원리
1. 비밀 키 (Secret Key)
2. 해시 알고리즘 (예: SHA-256)
이 두 가지를 조합하여 고정된 길이의 해시값(MAC)을 생성하여
수신자가 동일한 키로 다시 계산하여 데이터 변조 여부를 검증
검증 시 동일한 키를 사용하여야 되므로 대칭키 방식이며 해시 함수가 충돌을 방지하여 안정성이 확보됨.
코드예제
import javax.annotation.PostConstruct;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import lombok.Synchronized;
import org.apache.commons.codec.binary.Base64;
public class SecurityUtil {
// 대칭 키: 실제 운영에서는 안전한 난수 값으로 대체해야 합니다.
private static final String SYMMETRIC_KEY = "symmetericKey";
private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
private static Mac mac;
/**
* 클래스 초기화 시 HMAC 해시 객체를 한 번만 생성하고 초기화합니다.
*/
@PostConstruct
public void init() throws Exception {
mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
mac.init(new SecretKeySpec(SYMMETRIC_KEY.getBytes("utf-8"), HMAC_SHA256_ALGORITHM));
}
/**
* 메시지를 HMAC-SHA256 방식으로 해싱하고 Base64로 인코딩합니다.
* 동기화로 멀티스레드 환경에서도 안전하게 동작합니다.
*/
@Synchronized
public static String encodeHMacSha256(final String message) {
byte[] hash = mac.doFinal(message.getBytes());
return Base64.encodeBase64String(hash);
}
}•@PostConstruct는 Bean 생성 후 호출되어 Mac 객체를 한 번만 초기화
•Mac 객체는 스레드에 안전하지 않기 때문에 @Synchronized로 doFinal() 호출을 보호합니다.
•실제 운영 환경에서는 SYMMETRIC_KEY를 고정된 문자열이 아니라 안전하게 관리되는 키 저장소 또는 환경변수로부터 주입받는 것이 좋음
Syncronized 사용이유
Mac 객체는 내부 상태를 갖고 있어서 doFinal()이 호출되면 상태가 변경된다.
여러 스레드가 동시에 doFinal()을 호출할 경우 충돌이 발생하거나 예외가 발생할 수 있으므로, 반드시 동기화 처리가 필요.
참고사항
https://docs.oracle.com/javase/8/docs/api/javax/crypto/Mac.html
Mac (Java Platform SE 8 )
Finishes the MAC operation. A call to this method resets this Mac object to the state it was in when previously initialized via a call to init(Key) or init(Key, AlgorithmParameterSpec). That is, the object is reset and available to generate another MAC fro
docs.oracle.com
java 8버전 기준으로 doFinal 에 대한 설명을 보면 같은 키에 대해서는 여러번 재사용이 가능하다고 명시되어 있어 재사용 하였고, doFinal 사용 시 내부 상태가 변경되기 때문에 동기화 처리를 추가하였음.
'java > 내용정리' 카테고리의 다른 글
| 글로벌 시스템 만들 시 주의사항 : 국가별 시간 관리 (Java + Oracle + JavaScript) (1) | 2025.06.14 |
|---|---|
| JWT 정의 및 java 예제코드 정리 (0) | 2025.05.27 |
| Java : 문자열을 더하는 두 가지 방법 – StringBuilder.append() vs String 덧셈(+) 차이점 (0) | 2025.05.23 |
| 프로젝트 내부 이미지 파일 불러와서 html 파일 입력창에 등록 후 서버로 전송하기 (0) | 2025.05.19 |
| 이미지url을 업로드하자! URL형식 이미지 → HTML 업로드 처리 (2) | 2025.05.17 |
