iT邦幫忙

2021 iThome 鐵人賽

DAY 7
1
永豐金融APIs

openAPI 對接實務系列 第 7

[day7] API回覆內容(Response)解析 & 驗證(sign)

訊息文本AES CBC 解密

將昨天產生產生的訊息文本,傳送至測試伺服器https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Order,如果沒有問題的話會收到如下訊息:

{
  "Version": "1.0.0",
  "ShopNo": "NA0249_001",
  "APIService": "OrderCreate",
  "Sign": "FBF41F9BFA5607F5141D508DA6B914DCAB97CE7CDE22EE636C9FDD81A9AC3277",
  "Nonce": "NjM3Njc0NzY1MDExMjc6MjVmMDE2NDUyYThjNDE4ODY1MmI4Mzk0OGM3YWY1Mjg0M2Y4NDdlMjAyMTk0YWM3MGFhZGZmNDcyMGQ5ZjhjNw==",
  "Message": "FAE9E297DB4D0B16D3E5A56561B28DDC41CD135B23D5309F971091033425405BA96669FE5A27B1D42DC4EB9636EBB0D9D9D618BC2B3969124A4F73CBB760CB83084623E1C2DED846BB46525E1B74F187EEF42A0F483AC49B0A12268D28452F44D268D38BDB91C464B74B1BB80D6DFC372622D8006005B0ABF5637287CB587FCE6ABB9D2BA377A29EC2E7E696CFDE2E305739CF2E6CBC1F2B71741064CA21CE3A6C6BFBAD663140A4CCC5AB24BE77569A26E1EA3A71EC2BC7AFC6E0F43ED537E42CDF535E910E25413BF4BC649D800F592FEA277BA18BF312EDD9A062D7F24A6405AC01EEF3F7F55EBC5978EEFC7AB097A802A1D05B675CC08E5ABD3FD9106EE0C624839EB0451EBE0F10E85C6DFCE4C9E0D29B3E633928F1A73102C04FA9DB91D7391D8917DC263437DBC50A7ACAF2CA06F8114669F783EF5189925B61EC9D7ECC3C504D09996665BC7CD3C3725F5D778F1D843FC42183153E565BF06307405F30401BA7E83EFAC91B54612D92E284F3BCFE324E26F8E7BFB1AE6326D96E2513D53A4D25DC1C1C24437A403A5BF281DA95A4EE018D6224F18128EB5FBFF3EF73EEE19E0EFD9429AB2976AB70C8DB050EFD81DB591831FD820157ACC4B60F101ABEF75AAA420EFF4C6FD7495226872B410CB87E958E7C92BFF3E3D35B4367B927F167A30F495876E82428ACC0D51BD61C7D30DB0A5FAEA26CDA24F79EA132000FFCF9C2E6EBCA124C6D8F546DF954C333"
}

通過先前產生訊息文本的相同方式進行解密(但本回Nonce由回應中的欄位取得),分別得出

  • Nonce:NjM3Njc0NzY1MDExMjc6MjVmMDE2NDUyYThjNDE4ODY1MmI4Mzk0OGM3YWY1Mjg0M2Y4NDdlMjAyMTk0YWM3MGFhZGZmNDcyMGQ5ZjhjNw==
  • AESKey(HashID):87282A2FA0E209EBE1B3713AB56A06C2
  • IV:15FE64E9D28D8934

將Message欄位取出,以Hex方式讀取,同樣用AES-CBC(256bits)方式進行解密,會得出

{"OrderNo":"2021091500002","ShopNo":"NA0249_001","TSNo":"NA024900000173","Amount":40400,"Status":"S","Description":"S0000 – 處理成功","Param1":"","Param2":"","Param3":"","PayType":"C","CardParam":{"CardPayURL":"https://sandbox.sinopac.com/QPay.WebPaySite/Bridge/PayCard?TD=NA024900000173&TK=a135ccb2-2e4c-4af4-b4ef-dfece6367731"}}

以Python實作解密Message

def AES_CBC_Decrypt(HashID, iv, data):
  try:
    key = str.encode(HashID)
    iv = str.encode(iv)
    data = bytes.fromhex(data)
    cipher = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
    pt = unpad(cipher.decrypt(data), AES.block_size)
    return pt.decode("utf-8")
  except (ValueError, KeyError):
    print("Incorrect decryption")

undecrypt_msg = js_resp['Message']
msg = AES_CBC_Decrypt(HashID, iv=iv, data=undecrypt_msg)

驗算Respnse中Sign

看到這邊,你一定想哇成功了對不對,其實還沒有,還少了最後一步,簽名驗證(Sign),收發的資訊傳輸過程都需要驗證訊息文本與金鑰的配對,以確認資料的防竄改,關於如何產生Sign,可以參照Day4,在這邊我們要取訊息文本中的Sign,與解密後Message中的參數進行比對是否相同

以Python實作驗算Response Sign

def GetRespSign(msg:str, nonce:str, HashID:str):
  msg = json.loads(msg)
  SignStr = ""
  for parm in sorted(msg, key=lambda v: v.upper()):
      val = msg[parm]
      if(type(val) == dict or not val):continue
      SignStr = SignStr + f"{parm}={msg[parm]}&"
  SignStr = SignStr.removesuffix('&') + nonce + HashID
  sign = hashlib.sha256(SignStr.encode('utf-8')).hexdigest().upper()
  return sign

resp_vsign = js_resp['Sign']
resp_csign = GetRespSign(msg=msg, nonce=nonce, HashID=HashID)

if(resp_vsign == resp_csign):
  print("簽章檢驗成功")
  return true
else:
  print("簽章驗證失敗")
  return false

呼,到現在已經將大致上的API如何使用完成時做了,接下來準備小跑進入實作環節,搭建一個儲值卡系統希望有時間做得完


上一篇
[day6] AES-CBC 內文加密機制(Message)
下一篇
[day8] 實務搭建 - 儲值卡,系統概述
系列文
openAPI 對接實務30

尚未有邦友留言

立即登入留言