HMAC
Hash Message Authentication Code
auth hash crypto
HMAC
서버가 클라이언트로부터 요청을 받을때, 이 요청이 신뢰할 수 있는지 확인하기 위해서 HMAC(Hash Message Authentication Code) 을 사용한다.
예를들어, 특정 파라미터를 받아서 단순히 검증 여부를 확인하고 아무런 데이터를 응답하지 않는 API 의 경우에는 클라이언트가 신뢰할 수 있는 요청을 보냈는지에 대한 검증 필요성이 낮지만, 만약 특정 페이로드를 받아서 데이터를 저장하거나, 정보를 조회하여 응답해주는 경우에는 신뢰할 수 있는 요청인지에 대한 검증 을 해야 한다.
HMAC 을 사용한 인증 방식은 다음과 같다.
- 동일한 secretKey 를 서버와 클라이언트가 나눠 갖는다.
- 클라이언트는 해시 알고리즘(e.g SHA-256)과 SecretKey 를 활용하여 특정 메시지를 해시한다.
- 클라이언트는 서버로 x-gw-signature 등의 헤더키와 암호화된 메시지를 헤더에 담아서, 데이터와 함께 전송한다.
- 서버는 클라이언트로 전달받은 해시값과, 클라이언트로 전달 받은 데이터를 해시 알고리즘(e.g SHA-256)과 SecretKey 를 활용하여 특정 메시지를 해시하여 비교한다.
- 두 값이 같은 경우에 정상 요청으로 간주한다.
JDK APIs 를 이용하여 다음과 같이 구현할 수 있다.
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class HMACUtil {
public static String initialize(String algorithm, String data, String key)
throws NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKeySpec);
return bytesToHex(mac.doFinal(data.getBytes()));
}
public static String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (byte h : hash) {
String hex = Integer.toHexString(0xff & h);
if (hex.length() == 1)
hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}
테스트 코드는 다음과 같다.
@Test
public void hmacTest() throws NoSuchAlgorithmException, InvalidKeyException {
String hmacSHA256Value = "5b50d80c7dc7ae8bb1b1433cc0b99ecd2ac8397a555c6f75cb8a619ae35a0c35";
String hmacSHA256Algorithm = "HmacSHA256";
String data = "hoxy";
String key = "123456";
String result = HMACUtil.initialize(hmacSHA256Algorithm, data, key);
assertEquals(hmacSHA256Value, result);
}
Guarantee of Integrity
- [TIP 1] 어떤 요청이 중간에 해커에게 가로채어져서 변조되었다면, 이 요청은 '무결성'이 보장되지 않았다고 한다. 요청의 전체 메세지에 대한 무결성을 보장하고 싶다면 요청 내용의 전문을 가지고 hash 를 생성하는 것이 좋다.
- [TIP 2] 실제 클라에서 정상적으로 생성된 hash 를 해커가 그대로 재사용하는 것을 막기 위해 hash 를 생성할 때 timestamp 를 포함하는 것이 좋다.