인증 헤더 만들기
인증 헤더 만들기
Private API 요청 시 발급받은 Connect Key와 Secret Key를 이용하여 4개의 파라미터를 헤더에 추가하여 전송합니다.
요청 변수 | 설명 | 타입 |
---|---|---|
api-client-type | Api-Sign 생성 시 사용하는 구분자 유형 "0" : Ascii Code 0 (기본값) "1" : Ascii Code 1 "2" : ";" (semicolon) | String/선택 |
Api-Key | 사용자 API Key | String/필수 |
Api-Nonce | 현재의 시간을 밀리초(millisecond, ms)로 표현한 값 (예 : "1655280216476") | String/필수 |
Api-Sign | End Point + Request Parameter + Api-Nonce + 사용자 Secret Key를 조합하여 인코딩한 값 | String/필수 |
생성된 인증 헤더의 페이로드 예시입니다.
{
"api-client-type" : "2",
"Api-Sign" : "OGU1NWYxYjRmYmJjMzRmZWIyM2FkMGFkghhvbDGVSDgweY2ZkYzE2MzIwY2I2OGZlZmViOGY2OWE1MDFlZmNiZWMyNTBjOWY5NWRhMTQyZDdkOTFlNzQzZGUwNTM1MjJjMDdjNTEGgvdw2EyOTMwMzVkNzc=",
"Api-Nonce" : "1655282125050",
"Api-Key" : "3095809fgv90t4hnf82fls9023rlasf023nl"
}
Api-Sign 생성 안내
-
Request Parameter 조합
- endpoint 파라미터 추가
endpoint=/info/balance&order_currency=BTC&payment_currency=KRW
- URL 인코딩
endpoint=%2Finfo%2Fbalance&order_currency=BTC&payment_currency=KRW
- endpoint 파라미터 추가
-
End Point + Request Parameter + Api-Nonce 조합
- api-client-type에서 지정한 구분자를 이용해 3개의 값을 조합합니다.
/info/balance;endpoint=%2Finfo%2Fbalance&order_currency=BTC&payment_currency=KRW;1655283111604
- api-client-type에서 지정한 구분자를 이용해 3개의 값을 조합합니다.
-
HmacSha512 알고리즘으로 인코딩
- 2번에서 조합한 값을 사용자의 Secret Key를 이용하여 인코딩합니다.
-
Base64 인코딩
- HmacSha512로 인코딩한 값을 다시 Base64로 인코딩합니다.
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import org.codehaus.jackson.map.ObjectMapper;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
@SuppressWarnings("unused")
public class Api_Client {
protected String api_url = "https://api.bithumb.com";
protected String api_key;
protected String api_secret;
public Api_Client(String api_key, String api_secret) {
this.api_key = api_key;
this.api_secret = api_secret;
}
private String usecTime() {
return String.valueOf(System.currentTimeMillis());
}
private String request(String strHost, String strMemod, HashMap<String, String> rgParams,
HashMap<String, String> httpHeaders) {
String response = "";
// SSL
if (strHost.startsWith("https://")) {
HttpRequest request = HttpRequest.get(strHost);
// Accept all certificates
request.trustAllCerts();
// Accept all hostnames
request.trustAllHosts();
}
if (strMemod.toUpperCase().equals("HEAD")) {
} else {
HttpRequest request = null;
// POST/GET
if (strMemod.toUpperCase().equals("POST")) {
request = new HttpRequest(strHost, "POST");
request.readTimeout(10000);
System.out.println("POST ==> " + request.url());
if (httpHeaders != null && !httpHeaders.isEmpty()) {
httpHeaders.put("api-client-type", "2");
request.headers(httpHeaders);
System.out.println(httpHeaders.toString());
}
if (rgParams != null && !rgParams.isEmpty()) {
request.form(rgParams);
System.out.println(rgParams.toString());
}
} else {
request = HttpRequest.get(strHost
+ Util.mapToQueryString(rgParams));
request.readTimeout(10000);
System.out.println("Response was: " + response);
}
if (request.ok()) {
response = request.body();
} else {
response = "error : " + request.code() + ", message : "
+ request.body();
}
request.disconnect();
}
return response;
}
public static String encodeURIComponent(String s) {
String result = null;
try {
result = URLEncoder.encode(s, "UTF-8")
.replaceAll("\\+", "%20")
.replaceAll("\\%21", "!")
.replaceAll("\\%27", "'")
.replaceAll("\\%28", "(")
.replaceAll("\\%29", ")")
.replaceAll("\\%26", "&")
.replaceAll("\\%3D", "=")
.replaceAll("\\%7E", "~");
}
// This exception should never occur.
catch (UnsupportedEncodingException e) {
result = s;
}
return result;
}
private HashMap<String, String> getHttpHeaders(String endpoint, HashMap<String, String> rgData,
String apiKey, String apiSecret) {
String strData = Util.mapToQueryString(rgData).replace("?", "");
String nNonce = usecTime();
strData = strData.substring(0, strData.length() - 1);
System.out.println("1 : " + strData);
strData = encodeURIComponent(strData);
System.out.println("2 : " + strData);
HashMap<String, String> array = new HashMap<String, String>();
String str = endpoint + ";" + strData + ";" + nNonce;
String encoded = asHex(hmacSha512(str, apiSecret));
System.out.println("strData was: " + str);
System.out.println("apiSecret was: " + apiSecret);
array.put("Api-Key", apiKey);
array.put("Api-Sign", encoded);
array.put("Api-Nonce", String.valueOf(nNonce));
return array;
}
private static final String DEFAULT_ENCODING = "UTF-8";
private static final String HMAC_SHA512 = "HmacSHA512";
public static byte[] hmacSha512(String value, String key) {
try {
SecretKeySpec keySpec = new SecretKeySpec(
key.getBytes(DEFAULT_ENCODING),
HMAC_SHA512);
Mac mac = Mac.getInstance(HMAC_SHA512);
mac.init(keySpec);
final byte[] macData = mac.doFinal(value.getBytes());
byte[] hex = new Hex().encode(macData);
return hex;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static String asHex(byte[] bytes) {
return new String(Base64.encodeBase64(bytes));
}
@SuppressWarnings("unchecked")
public String callApi(String endpoint, HashMap<String, String> params) {
String rgResultDecode = "";
HashMap<String, String> rgParams = new HashMap<String, String>();
rgParams.put("endpoint", endpoint);
if (params != null) {
rgParams.putAll(params);
}
String api_host = api_url + endpoint;
HashMap<String, String> httpHeaders = getHttpHeaders(endpoint, rgParams, api_key, api_secret);
rgResultDecode = request(api_host, "POST", rgParams, httpHeaders);
if (!rgResultDecode.startsWith("error")) {
// json
HashMap<String, String> result;
try {
result = new ObjectMapper().readValue(rgResultDecode,
HashMap.class);
System.out.println(result.get("status"));
} catch (IOException e) {
e.printStackTrace();
}
}
return rgResultDecode;
}
}
import time
import math
import base64
import hmac, hashlib
import urllib.parse
import requests
class XCoinAPI:
api_url = "https://api.bithumb.com";
api_key = "";
api_secret = "";
def __init__(self, api_key, api_secret):
self.api_key = api_key;
self.api_secret = api_secret;
def body_callback(self, buf):
self.contents = buf;
def microtime(self, get_as_float = False):
if get_as_float:
return time.time()
else:
return '%f %d' % math.modf(time.time())
def usecTime(self) :
mt = self.microtime(False)
mt_array = mt.split(" ")[:2];
return mt_array[1] + mt_array[0][2:5];
def xcoinApiCall(self, endpoint, rgParams):
# 1. Api-Sign and Api-Nonce information generation.
# 2. Request related information from the Bithumb API server.
#
# - nonce: it is an arbitrary number that may only be used once.
# - api_sign: API signature information created in various combinations values.
endpoint_item_array = {
"endpoint" : endpoint
}
uri_array = dict(endpoint_item_array, **rgParams) # Concatenate the two arrays.
str_data = urllib.parse.urlencode(uri_array)
nonce = self.usecTime()
data = endpoint + chr(0) + str_data + chr(0) + nonce
utf8_data = data.encode('utf-8')
key = self.api_secret
utf8_key = key.encode('utf-8')
h = hmac.new(bytes(utf8_key), utf8_data, hashlib.sha512)
hex_output = h.hexdigest()
utf8_hex_output = hex_output.encode('utf-8')
api_sign = base64.b64encode(utf8_hex_output)
utf8_api_sign = api_sign.decode('utf-8')
headers = {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
"Api-Key": self.api_key,
"Api-Nonce": nonce,
"Api-Sign": utf8_api_sign
}
url = self.api_url + endpoint
r = requests.post(url, headers=headers, data=rgParams)
return r.json()
const request = require("request");
const hmacSHA512 = require("crypto-js/hmac-sha512");
const XCoinAPI = class {
constructor(api_key, api_secret) {
this.apiUrl = "https://api.bithumb.com";
this.api_key = api_key;
this.api_secret = api_secret;
}
xcoinApiCall(endPoint, params) {
let rgParams = {
endPoint: endPoint,
};
if (params) {
for (let o in params) {
rgParams[o] = params[o];
}
}
const api_host = this.apiUrl + endPoint;
const httpHeaders = this._getHttpHeaders(
endPoint,
rgParams,
this.api_key,
this.api_secret
);
const options = {
method: "POST",
url: api_host,
headers: httpHeaders,
form: rgParams,
};
return new Promise(function (resolve, reject) {
request(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
resolve(response);
} else {
reject(error);
}
});
});
}
_getHttpHeaders(endPoint, rgParams, api_key, api_secret) {
let strData = http_build_query(rgParams);
let nNonce = this.usecTime();
return {
"Api-Key": api_key,
"Api-Sign": base64_encode(
hmacSHA512(
endPoint + chr(0) + strData + chr(0) + nNonce,
api_secret
).toString()
),
"Api-Nonce": nNonce,
};
}
usecTime() {
let rgMicrotime = microtime().split(" "),
usec = rgMicrotime[0],
sec = rgMicrotime[1];
usec = usec.substr(2, 3);
return Number(String(sec) + String(usec));
}
};
const microtime = (get_as_float) => {
// discuss at: http://phpjs.org/functions/microtime/
// original by: Paulo Freitas
// example 1: timeStamp = microtime(true);
// example 1: timeStamp > 1000000000 && timeStamp < 2000000000
// returns 1: true
const now = new Date().getTime() / 1000;
const s = parseInt(now, 10);
return get_as_float ? now : Math.round((now - s) * 1000) / 1000 + " " + s;
};
const http_build_query = (obj) => {
let output_string = [];
Object.keys(obj).forEach((val) => {
let key = val;
key = encodeURIComponent(key.replace(/[!'()*]/g, escape));
if (typeof obj[val] === "object") {
let query = build_query(obj[val], null, key);
output_string.push(query);
} else {
let value = encodeURIComponent(obj[val].replace(/[!'()*]/g, escape));
output_string.push(key + "=" + value);
}
});
return output_string.join("&");
};
const base64_encode = (data) => {
// discuss at: http://phpjs.org/functions/base64_encode/
// original by: Tyler Akins (http://rumkin.com)
// improved by: Bayron Guevara
// improved by: Thunder.m
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: Rafał Kukawski (http://kukawski.pl)
// bugfixed by: Pellentesque Malesuada
// example 1: base64_encode('Kevin van Zonneveld');
// returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
// example 2: base64_encode('a');
// returns 2: 'YQ=='
const b64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
let o1,
o2,
o3,
h1,
h2,
h3,
h4,
bits,
i = 0,
ac = 0,
enc = "",
tmp_arr = [];
if (!data) {
return data;
}
do {
// pack three octets into four hexets
o1 = data.charCodeAt(i++);
o2 = data.charCodeAt(i++);
o3 = data.charCodeAt(i++);
bits = (o1 << 16) | (o2 << 8) | o3;
h1 = (bits >> 18) & 0x3f;
h2 = (bits >> 12) & 0x3f;
h3 = (bits >> 6) & 0x3f;
h4 = bits & 0x3f;
// use hexets to index into b64, and append result to encoded string
tmp_arr[ac++] =
b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
} while (i < data.length);
enc = tmp_arr.join("");
const r = data.length % 3;
return (r ? enc.slice(0, r - 3) : enc) + "===".slice(r || 3);
};
const chr = (codePt) => {
// discuss at: http://phpjs.org/functions/chr/
// original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: Brett Zamir (http://brett-zamir.me)
// example 1: chr(75) === 'K';
// example 1: chr(65536) === '\uD800\uDC00';
// returns 1: true
// returns 1: true
if (codePt > 0xffff) {
// Create a four-byte string (length 2) since this code point is high
// enough for the UTF-16 encoding (JavaScript internal use), to
// require representation with two surrogates (reserved non-characters
// used for building other characters; the first is "high" and the next "low")
codePt -= 0x10000;
return String.fromCharCode(
0xd800 + (codePt >> 10),
0xdc00 + (codePt & 0x3ff)
);
}
return String.fromCharCode(codePt);
};
module.exports.XCoinAPI = XCoinAPI;
샘플 소스 전체 다운로드
Updated almost 2 years ago