인증
모든 API 요청은 API Key + HMAC-SHA256 서명 방식으로 인증합니다.
필수 헤더
| 헤더 | 설명 |
|---|---|
X-API-Key | 대시보드에서 발급받은 API 키 (kb_...) |
X-Timestamp | 요청 시각 (Unix 밀리초, Date.now()) |
X-Signature | HMAC-SHA256 서명 (hex 인코딩) |
caution
X-Timestamp는 서버 시각 기준 ±5분 이내여야 합니다. 초과 시 401 Timestamp outside tolerance window 오류가 발생합니다.
서명 생성 방법
1. 서명 대상 문자열 구성
{METHOD}\n{PATH}\n{TIMESTAMP}\n{BODY_HASH}
| 항목 | 설명 |
|---|---|
METHOD | 대문자 HTTP 메서드 (GET, POST 등) |
PATH | 쿼리스트링 제외 경로 (예: /certificates) |
TIMESTAMP | X-Timestamp 값과 동일 |
BODY_HASH | 요청 바디의 SHA-256 hex 값. 바디 없으면 빈 문자열의 해시 |
2. HMAC-SHA256 서명
X-API-Secret (API 시크릿 키, kbs_...)으로 위 문자열을 HMAC-SHA256 서명합니다.
3. 요청 바디 해시 규칙
| 요청 유형 | BODY_HASH 계산 방법 |
|---|---|
| JSON 바디 있음 | SHA256(JSON.stringify(body)) — 공백 없이 직렬화 |
| 바디 없음 (GET 등) | SHA256("") — 빈 문자열 해시 |
| multipart/form-data (이미지 업로드) | SHA256("") — 빈 문자열 해시 |
코드 예시
- TypeScript
- Python
import crypto from 'crypto';
function sign(
method: string,
path: string,
timestamp: string,
body: Record<string, unknown> | null,
secret: string,
): string {
const bodyHash = crypto
.createHash('sha256')
.update(body ? JSON.stringify(body) : '')
.digest('hex');
const stringToSign = `${method}\n${path}\n${timestamp}\n${bodyHash}`;
return crypto
.createHmac('sha256', secret)
.update(stringToSign)
.digest('hex');
}
async function apiRequest(
method: string,
path: string,
body?: Record<string, unknown>,
) {
const apiKey = 'kb_your_api_key';
const apiSecret = 'kbs_your_api_secret';
const timestamp = String(Date.now());
const signature = sign(method, path, timestamp, body ?? null, apiSecret);
const res = await fetch(`https://api.koreabaas.co.kr${path}`, {
method,
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
'X-Timestamp': timestamp,
'X-Signature': signature,
},
body: body ? JSON.stringify(body) : undefined,
});
return res.json();
}
import hmac
import hashlib
import time
import json
import requests
API_KEY = 'kb_your_api_key'
API_SECRET = 'kbs_your_api_secret'
BASE_URL = 'https://api.koreabaas.co.kr'
def sign(method: str, path: str, body: dict | None = None) -> tuple[str, str]:
timestamp = str(int(time.time() * 1000))
body_str = json.dumps(body, separators=(',', ':'), ensure_ascii=False) if body else ''
body_hash = hashlib.sha256(body_str.encode('utf-8')).hexdigest()
string_to_sign = f'{method}\n{path}\n{timestamp}\n{body_hash}'
signature = hmac.new(API_SECRET.encode(), string_to_sign.encode(), hashlib.sha256).hexdigest()
return timestamp, signature
def api(method: str, path: str, body: dict | None = None):
timestamp, signature = sign(method, path, body)
headers = {
'X-API-Key': API_KEY,
'X-Timestamp': timestamp,
'X-Signature': signature,
}
if body:
headers['Content-Type'] = 'application/json'
res = requests.request(
method,
BASE_URL + path,
headers=headers,
data=json.dumps(body, separators=(',', ':'), ensure_ascii=False).encode('utf-8') if body else None,
)
return res.json()
note
JSON.stringify 직렬화 시 공백(space) 없이 직렬화해야 서명이 일치합니다.
Python에서는 json.dumps(body, separators=(',', ':'), ensure_ascii=False)를 사용하세요.
오류 코드
| HTTP | 코드 | 설명 |
|---|---|---|
| 401 | — | Missing headers |
| 401 | — | Invalid X-Timestamp |
| 401 | — | Timestamp outside tolerance window |
| 401 | — | Invalid API key |
| 401 | — | Signature mismatch |
| 401 | — | API key not active / expired |