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 14 - 安装与执行 YOLO

作为CISO最关键的任务-开发资讯安全性管理系统

在这四个选项中,开发信息安全管理系统(ISMS)是最合适,最关键的。ISMS从管理承诺和政策开始,这...

DAY 18 Big Data 5Vs – Variety(速度) EMR (1)

Amazon Elastic MapReduce(EMR)是可以在EC2 instance 或 Am...

【Day30】我结束,换你了

挖,我居然真的完成这 30 天发文的活动,我觉得我好棒棒 XD。 统整 30 天的内容 给 Azur...

【第十五天 - SSRF】

Q1. 什麽是 SSRF? SSRF (Server Side Request Forgery),也...

laravel 8 (一) 建立专案及资料库设定

安装专案 composer global require laravel/installer //将...