[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如何使用完成时做了,接下来准备小跑进入实作环节,搭建一个储值卡系统希望有时间做得完


<<:  #04 No-code 之旅 — Next.js 中的 Pre-render 与 Data Fetching

>>:  JavaScript Day04 - 变数(3)

从零开始-30日练习开发iOS APP-UserDefault Day-28

正文: 利用 UserDefault 储存资料 预览图: 程序码: import UIKit cla...

ISO 27001 资讯安全管理系统 【解析】(十二)

资通安全责任等级 依照资通安全责任等级分级办法,由主管机关核定相对应之等级,按照等级决定导入系统之...

D26 - 「来互相伤害啊!」:站在 Phaser 的肩膀上

鳕鱼:「再来要设计对战游戏,可以切换场景,人物可以在场地随意移动,发射武器互相攻击,人物会与墙壁、敌...

[Day07]打造专业稽核形象

从上图可知衣装仪态的重要性。 其实,稽核专业的形象,一直是我所追求的目标,即使我在资安顾问的年资已...

Day 05:到底有多坏?演算法的最坏情况执行时间

讨论演算法的执行时间到现在,我们只提最糟糕的情况,好像不断在强调演算法的效能可以有多差。 你可能会想...