iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
永豐金融APIs

試著讀懂與串接永豐金融APIs系列 第 4

Day 0x4 - 請求 API 前的前置動作(Part 1)[Nonce, Hash Id, Sign]

0x1 Nonce 取得

Nonce 為每次發出請求API服務前必須取得的參數之一,
而這個是需要在請求前要先請求的API,且其時效性只有60秒
好我知道這樣講有點混亂,
依照自己的解釋簡單來說 前置API動作
請求其他API前,必定要先取得這個,取得這個也很簡單,提供商店代號就行,
但要注意的是發出下一個請求的IP要一致,但自己是覺得在同一台伺服器是不用太擔心

這裡用範例程式來改,
因為 post_data 需要轉譯成 json,回應後直接解譯,
不用每次都要寫 json_encode 或 json_decode
程式碼如下:
這裡 url 是隱藏的,看起來應該不能把 API 網址直接打出來,各位需要的話再自行修改吧

$nonce_url = 'https://localhost/api/get_nonce';

function WebApi($url, $post_data) {
    $ch=curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_HEADER => 0,
        CURLOPT_NOBODY => 1,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_HTTPHEADER => ["Content-type: application/json; charset=utf-8"],
        CURLOPT_POST=> 1,
        CURLOPT_POSTFIELDS=> json_encode($post_data),
        CURLOPT_SSL_VERIFYPEER => 0,
        CURLOPT_SSL_VERIFYHOST => 0,
        CURLOPT_SSLVERSION => 6
    ]);
    $result=curl_exec($ch);
    curl_close($ch);

    return json_decode($result, true);
}

$responce = WebApi($nonce_url, ['ShopNo' => "ITBON_001"]);
// Response 內容
//  [ "Nonce" => "不是這個字串,是Encode 的內容" ]

0x2 產生 Sign 簽章

0x21 Hash Id 計算

官方提供四組雜湊值,分別為 a1, a2, b1, b2(皆為 0~9,A-F 的十六進位字串),
依照 a 對 a,b 對 b 做 XOR運算,
再把兩個結果組合起來,並把英文字母轉大寫

官方提供的是逐個算,而自己有發現兩個好用的函數,
分別是 hexdec 跟 dechex,缺點是一次只能 4 個字元(bytes),
寫法因人而異,這裡提供我的寫法,有興趣的各位歡迎在調整。

$a1 = '1234567890ABCDEF';
$a2 = 'ABCDEF1234567890';
$b1 = '0987654321FEDCBA';
$b2 = 'FEDCBA0987654321';

function calcHashId($a1, $a2, $b1, $b2) {
    $a = $b = '';
    $length = strlen($a1);
    for($i = 0; $i < $length; $i+=4) {
        $a .= dechex(hexdec(substr($a1, $i, 4)) ^ hexdec(substr($a2, $i, 4)));
        $b .= dechex(hexdec(substr($b1, $i, 4)) ^ hexdec(substr($b2, $i, 4)));
    }
    return strtoupper($a . $b);
}

calcHashID($a1, $a2, $b1, $b2);
// result: "B9F9B96AA4FDB57FF75BDF4AA69B9F9B";

0x22 Sign (安全簽章) 計算

  • 產出安全簽章前必須要先取得 Nonce、Hash ID、訊息內文,才可計算出安全簽章
  • 以下幾點注意事項
    1. 移除空值參數,參數值前後皆不可空白
    2. 將剩餘所有參數值依照「參數名稱」由小至大排序(不分大小寫即 A < B and a < B ),組成如 param1=value1&param2=value2 的字串
    3. 如為多節點參數則不參與 sign 值演算
    4. 最後使用 SHA256 進行計算

第二點比較麻煩一些,一開始以為是依照 ASCII 的順序,看到 a < B,就發現不太對勁,要處理的程序會多一些
而第三點只需要把內容為 array 的都排除,這點倒是還好

假設要傳送的內容如下(文件範例為JSON,這裡有改成PHP的陣列)

$data = [
    "ShopNo"            => "BA0026_001",
    "OrderNo"           => "A202109140001",
    "Amount"            => 16888,
    "CurrencyID"        => "TWD",
    "PayType"           => "A",
    "ATMParam"          => ["ExpireDate" => "20210921"],
    "CardParam"         => [],
    "ConvStoreParam"    => [],
    "PrdtName"          => "虛擬帳號訂單",
    "ReturnURL"         => "https://127.0.0.1/Store/Return",
    "BackendURL"        => "https://127.0.0.1/AutoPush/PushSuccess"
];

輸出 Sign (安全簽章) 的程式如下

function generateSign($data, $nonce, $hash_id) {
    $keys = array_keys($data);
    foreach($keys as $key) {
        $value = array_shift($data);
        if (gettype($value) === 'array' || trim($value) === '') {
            continue;
        }

        $upper_key = strtoupper($key);
        $data[$upper_key] = "$key=$value";
    }
    
    ksort($data);
    $body = implode('&', $data) . $nonce . $hash_id;
    return strtoupper(hash('sha256', $body));
}

generateSign($data, 'test_nonce', 'test_hash_id');
// result: "1B97B8BD80C3B9DCA14343318BE30940247EEDD871F180F4C9230B6A5E5615AD"

0x3 今日小結

  • API 串接安全簽章與加密流程比想像中的還要長阿,
    Hash Id 用的函數 hexdec, dechex 寫到後面才發現長度不太對,
    找官方文件才發現只支援 4 bytes (32 bit)
    原本想放棄自幹一個出來,但後來還是硬著頭皮寫出來,
    至少讓自己清楚踩過什麼坑。
  • 在看內文雜湊那邊的時候卡了一下,想說有兩個字串是從哪邊來的,
    回頭看圖在發現原來是 Nonce 跟 Hash Id
    花了一些時間在上面,
    建議第21頁的字串雜湊標題可以多一句備註 Nonce 跟 Hash Id 要接在內文後面

上一篇
Day 0x3 - 閱讀API文件
下一篇
Day 0x5 - 請求 API 前的前置動作(Part 2)[IV, Message(AES-CBC)]
系列文
試著讀懂與串接永豐金融APIs30

尚未有邦友留言

立即登入留言