프로그래밍

Spring Boot와 HMAC을 활용한 데이터 서명 및 검증 구현

소행성왕자 2025. 3. 19. 19:48

개요

애플리케이션에서 데이터를 보호하고 무결성을 보장하기 위해 HMAC(Hash-based Message Authentication Code)를 활용할 수 있습니다. 본 글에서는 Spring Boot를 이용한 HMAC 기반의 전자서명 및 검증 API를 구현하는 방법을 설명합니다.

 

HMAC 이란?

HMAC은 키를 사용하여 데이터를 해싱하여 서명을 생성하는 방법입니다. HMAC을 사용하면 데이터가 변경되지 않았음을 검증할 수 있으며, 공유된 비밀 키를 통해 보안성을 유지할 수 있습니다.

HMAC-SHA256 알고리즘을 사용하여 JSON 데이터를 서명하고 검증하는 API를 구현합니다.

 

HMAC 서명 및 검증 API 구현

import org.springframework.web.bind.annotation.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class SecureSignatureController {

    private static final String SECRET_KEY = "my-secret-key"; // 🔐 서버 내부에서만 관리

    @GetMapping("/data")
    public Map<String, String> getSignedData() throws Exception {
        // 1. 응답 데이터
        String responseData = "{\"username\": \"user123\", \"balance\": 100}";

        // 2. HMAC SHA-256 서명 생성
        String signature = generateHMAC(responseData, SECRET_KEY);

        // 3. JSON 응답 반환
        Map<String, String> response = new HashMap<>();
        response.put("data", responseData);
        response.put("signature", signature);
        return response;
    }

    @PostMapping("/verify")
    public Map<String, Object> verifyData(@RequestBody Map<String, String> requestData) throws Exception {
        String receivedData = requestData.get("data");
        String receivedSignature = requestData.get("signature");

        // 서버에서 서명 다시 계산
        String computedSignature = generateHMAC(receivedData, SECRET_KEY);

        // 서명 검증 결과 반환
        Map<String, Object> response = new HashMap<>();
        response.put("valid", computedSignature.equals(receivedSignature));
        return response;
    }

    private String generateHMAC(String data, String key) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256");
        mac.init(secretKeySpec);
        byte[] hmacBytes = mac.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(hmacBytes);
    }
}

 

클라이언트(JavaScript)에서 API 호출 및 검증

async function fetchData() {
    // 서버에서 데이터 요청
    const response = await fetch('http://localhost:8080/api/data');
    const jsonResponse = await response.json();

    console.log("서버 응답 데이터:", jsonResponse);

    // 데이터를 서버로 다시 보내 검증 요청
    const verifyResponse = await fetch('http://localhost:8080/api/verify', {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(jsonResponse)
    });

    const verifyResult = await verifyResponse.json();

    if (verifyResult.valid) {
        console.log("✅ 데이터 정상:", JSON.parse(jsonResponse.data));
    } else {
        console.error("⛔ 데이터 변조 감지!");
    }
}

// 실행
fetchData();

 

동작 방식

  1. 클라이언트가 /api/data 엔드포인트를 호출하여 데이터를 요청합니다.
  2. 서버는 JSON 데이터를 생성하고, HMAC-SHA256을 사용하여 서명을 생성한 후 응답합니다.
  3. 클라이언트는 서버로부터 받은 데이터와 서명을 다시 /api/verify 엔드포인트에 보내어 검증합니다.
  4. 서버는 받은 데이터에 대해 서명을 다시 계산하고, 기존 서명과 비교하여 데이터 변조 여부를 판별합니다.
  5. 검증 결과를 클라이언트에 반환합니다.

'프로그래밍' 카테고리의 다른 글

높은 응집력과 느슨한 결합  (0) 2024.10.29
[mysql] 암복호화 방법  (0) 2023.07.03