產生HMAC Signature (簽章驗證)

與第三方串接API時,需要加密、MAC來驗證傳輸的內容是否正確沒有被傳輸中途修改過

開始前需要的資料

  1. KEY:雙方互相加解密的私鑰,第三方會提供一個KEY,通常會 base_64_encode,所以使用時,必須要先decode
  2. IV:隨機碼,為了每次加密時都是不一樣的內容,避免被暴力破解

如何產生 KEY?

  1. 請API服務方提供

如何建立 IV?

每次送出Request前產生的IV都會不同,為了避免被攔截查詢,

一些敏感或個資資料,會透過IV來進行加密的動作,接收方再進行解密取得資料

API方會提供產生IV的方法,一並加入Request Body中,透過IV驗證資料是否與MAC一致

建立範例

  • UUID SHA-256 之後 取前 16 Byte

    1
    2
    $uuid = Str::uuid()->toString();
    $iv = substr(hash('sha256', $uuid, true), 0, 16);
  • AES-256-CBC 加密

    1
    2
    3
    4
    5
    6
    7
    private function encryptAES256CBC(string $str, string $key, string $iv): string  
    {
    $cipher = 'AES-256-CBC';
    $encrypt = openssl_encrypt($str, $cipher, $key, OPENSSL_RAW_DATA, $iv);

    return $encrypt;
    }
  • AES-256-CBC 解密

    1
    2
    3
    4
    5
    6
    7
    private function decryptAES256CBC(string $data, string $key, string $iv): string
    {
    $cipher = 'AES-256-CBC';
    $decrypt = openssl_decrypt($data, $cipher, $key, OPENSSL_RAW_DATA, $iv);

    return $decrypt;
    }
  • 產生HMAC,文件會標示拿哪一些欄位進行加密檢核,流程大家都不一樣,以下的流程是

  1. 組合字串進行AES-256-CBC
  2. 字串 SHA-256 取得 前 16 Byte
  3. 前 16Byte 轉為字串,轉為全部大寫
1
2
3
4
5
6
7
8
$key = 'MmVhMmFlNjMtMWY1Ny00NjAzLTg4ZTUtNjYwMzViZTFkOTUz';
$data = [
'uuid' => 'bf2b29f6-cd80-4201-8615-7296eb8f74e4',
'order_no' => 'TED20230510',
'member_name' => '泰斯特',
'distance' => '300.0',
'mac' => '{MAC}',
];
  • 假設文件產生HMAC的順序為 $uuid + $order_no + $amount

加密前字串組合::bf2b29f6-cd80-4201-8615-7296eb8f74e4TED20230510300.0

注意欄位為非字串的話,可能會在組合時造成字串被忽略,300.0 在組合後會變成300

1
2
3
4
5
6
7
8
$uuid = 'bf2b29f6-cd80-4201-8615-7296eb8f74e4';
$key = 'MmVhMmFlNjMtMWY1Ny00NjAzLTg4ZTUtNjYwMzViZTFkOTUz';
$iv = substr(hash('sha256', $uuid, true), 0, 16);

$str = 'bf2b29f6-cd80-4201-8615-7296eb8f74e4TED20230510300.0';
$encrypt = $this->encryptAES256CBC($str, base64_decode($key), $iv);
$encrypt = substr(hash('sha256', $encrypt, true), 0, 16);
$mac = substr(Str::upper(bin2hex($encrypt)), 0, 16);
1
mac = C67FE772002FCCE0