Skip to main content

Signature อัลกอริทึมที่ใช้

1. รายละเอียด#

API Gateway จะตรวจสอบตัวตนที่เรียก API แต่ละรายการ และเซิร์ฟเวอร์จะตรวจสอบด้วยว่าพารามิเตอร์ที่ถูกเรียกนั้นถูกต้องหรือไม่ ดังนั้น HTTP request ในแต่ละรายการต้องมีข้อมูล signature หากมีการใช้ค่า signature ไม่ถูกต้อง รายการดังกล่าวจะถูกปฏิเสธการทำรายการ

API จะตรวจสอบตัวตนของร้านค้า โดยใช้โทเค็นที่กำหนดให้กับร้านค้า โทเค็นนั้นจะใช้เพื่อสร้าง signature ใน URL request HTTP และ signature ฝั่งเซิร์ฟเวอร์ ต้องเก็บเป็นความลับอย่าง

หากคุณเขียน HTTP request ด้วยตนเอง (แทนที่จะใช้ official SDK ที่ออกโดย ksher) คุณต้องเข้าใจอัลกอริทึม signature ต่อไปนี้

ขั้นตอนการสร้าง signature มีดังนี้:

  1. จัดเรียง request พารามิเตอร์ทั้งหมด (รวมถึงparam ใน query string แต่ยกเว้น "signature" และพารามิเตอร์ที่มีประเภท byte array) ให้เรียงสำดับตามชื่อพารามิเตอร์ในตาราง ASCII ตัวอย่างเช่น:
Before sort: foo=1, bar=2, foo_bar=3, foobar=4
After sort: bar=2, foo=1, foo_bar=3, foobar=4
  1. concat key:value pairs เช่น '=' ':' ',' ที่เชื่อมระหว่างแต่ละตัวแปร (เฉพาะนอกสุดตัวแปรเท่านั้น ค่าอื่นเช่น "chanel":"alipay,wechat" ให้คง "alipay,wechat" เอาไว้) แล้วเชื่อมพารามิเตอร์ที่เรียงลำดับแล้ว เข้าด้วยกันเป็นข้อความ ตัวอย่างเช่น:
bar2foo1foo_bar3foobar4
  1. เพิ่ม path API หน้าข้อความที่ต่อกัน ตัวอย่างเช่น เพิ่มชื่อ API "/test/api":
/test/apibar2foo1foo_bar3foobar4
  1. Encode ข้อความที่เชื่อมกันในรูปแบบ UTF-8 และสร้าง digest โดยสร้าง signature อัลกอริทึม (โดยใช้ HMAC_SHA256) ตัวอย่างเช่น:
hmac_sha256(/test/apibar2foo1foo_bar3foobar4)
  1. แปลง digest เป็นรูปแบบเลขฐานสิบหก ตัวอย่างเช่น:
hex("helloworld".getBytes("utf-8")) = "68656C6C6F776F726C64"

2. ตัวอย่าง#

โปรดดูตัวอย่างด้านล่างเกี่ยวกับวิธีการคำนวณ signature ตาม token (secret key) API และพารามิเตอร์

2.1. PYTHON#

def sign(secret,api, parameters):
#===========================================================================
# @param secret
# @param parameters
#===========================================================================
sort_dict = sorted(parameters)
parameters_str = "%s%s" % (api,
str().join('%s%s' % (key, parameters[key]) for key in sort_dict))
h = hmac.new(secret.encode(encoding="utf-8"), parameters_str.encode(encoding="utf-8"), digestmod=hashlib.sha256)
return h.hexdigest().upper()

2.2. JAVA#

/**
* Sign the API request with body.
*/
public static String signApiRequest(Map<String, String> params, String body, String appSecret, String signMethod, String apiName) throws IOException {
// first: sort all text parameters
String[] keys = params.keySet().toArray(new String[0]);
Arrays.sort(keys);
// second: connect all text parameters with key and value
StringBuilder query = new StringBuilder();
query.append(apiName);
for (String key : keys) {
String value = params.get(key);
if (areNotEmpty(key, value)) {
query.append(key).append(value);
}
}
// third:put the body to the end
if (body != null) {
query.append(body);
}
// next : sign the whole request
byte[] bytes = null;
if(signMethod.equals(Constants.SIGN_METHOD_HMAC)) {
bytes = encryptWithHmac(query.toString(), appSecret);
} else if(signMethod.equals(Constants.SIGN_METHOD_SHA256)) {
bytes = encryptHMACSHA256(query.toString(), appSecret);
}
// finally : transfer sign result from binary to upper hex string
return byte2hex(bytes);
}
private static byte[] encryptHMACSHA256(String data, String secret) throws IOException {
byte[] bytes = null;
try {
SecretKey secretKey = new SecretKeySpec(secret.getBytes(Constants.CHARSET_UTF8), Constants.SIGN_METHOD_HMAC_SHA256);
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
bytes = mac.doFinal(data.getBytes(Constants.CHARSET_UTF8));
} catch (GeneralSecurityException gse) {
throw new IOException(gse.toString());
}
return bytes;
}
/**
* Transfer binary array to HEX string.
*/
public static String byte2hex(byte[] bytes) {
StringBuilder sign = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toUpperCase());
}
return sign.toString();
}

2.3. C##

public static string SignRequest(IDictionary<string, string> parameters, string body, string appSecret, string signMethod, string apiName)
{
// first : sort all key with asci order
IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters, StringComparer.Ordinal);
// second : contact all params with key order
StringBuilder query = new StringBuilder();
query.Append(apiName);
foreach (KeyValuePair<string, string> kv in sortedParams)
{
if (!string.IsNullOrEmpty(kv.Key) && !string.IsNullOrEmpty(kv.Value))
{
query.Append(kv.Key).Append(kv.Value);
}
}
// third : add body to last
if (!string.IsNullOrEmpty(body))
{
query.Append(body);
}
// next : sign the string
byte[] bytes = null;
if (signMethod.Equals(Constants.SIGN_METHOD_SHA256))
{
HMACSHA256 sha256 = new HMACSHA256(Encoding.UTF8.GetBytes(appSecret));
bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(query.ToString()));
}
// finally : transfer binary byte to hex string
StringBuilder result = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
result.Append(bytes[i].ToString("X2"));
}
return result.ToString();
}

For the signature sample codes in other programming languages, refer to the source code of the official SDK.

3. ฉันจะหา token ได้ที่ไหน?#

คุณสามารถหา token (secret) สำหรับสร้าง signature ได้โดย login เข้าใช้เกตเวย์ API

picture 2

ในตัวอย่างข้างต้น 186d6c953c90f39c2973e6dd2e110d4057194996ef08fb4b3338180517b509c7 คือ token.

4. วิธีการดีบัก signature?#

ใน sandbox สามารถดีบัก ได้ 2 วิธีคือ

4.1. DO_NOT_CHECK#

คุณสามารถปิดใช้งานการตรวจสอบ signature เพื่อทดสอบโดยตั้งค่าเป็น "DO_NOT_CHECK" picture 1

4.2. Echo Signature Mode#

info

ถ้า Echo Signature Mode เปิดอยู่ API ทั้งหมดจะอยู่ใน echo โหมด ทำให้ทุก response สำหรับการเรียก API จะแสดงเฉพาะ signature ที่ถูกต้องและ string input ของ HMAC_SHA256 สำหรับการดีบัก

ภายใต้ Integration -> Configuration เมนูของเกตเวย์ คุณสามารถเปิดหรือปิดการตรวจสอบ Signature ได้

เมื่อเปิดโหมดก Echo Signature ทุกๆ request ที่ส่งมายัง API จะถูกใช้ในการตรวจสอบ Signature Signature ที่คำนวณแล้ว และอักขระเดิมก่อนจะถูกแฮชจะถูกส่งกลับไปยังไคลเอ็นต์เพื่อช่วยร้านค้าในการตรวจสอบ Signature

ต่อไปนี้คือตัวอย่าง response จากเปิดโหมด Echo Signature

{
"reference": "3D8E509148E6AF7C8DC4F6DC6C090026AAC78681F887B7B29D7DC011744B5330",
"locked": false,
"error_message": "The correct signature is in the reference field, raw data before sign in note field for your debug purpose.",
"cleared": false,
"error_code": "DEBUG",
"note": "/api/v1/redirect/orders/1621348784.4028008providerKshertimestampvalue2",
"signature": "8A6D48E38B99BED18C8A0C538B078D179A91E68FDA1413B80738272DE8624EEB",
"force_clear": false
}

ใน note จะมี string ดั้งเดิมก่อนที่จะการดำเนินการ HMAC-SHA256 และมีค่าแฮชที่คำนวณได้ในช่อง reference

5. Reference Design#