[day6]API串接-Message内文加密

在串接API时,遇到最大的坎就是Message内文加密了,
就让我们来试看看罗~

Message内文加密

项目 说明
产出JSON讯息内文 即要送出的讯息内文(JSON)
HashID 由四组 Hash 值透过两两 XOR 位元运算再相加的32 位元字串。
IV值 Nonce 值经过 SHA256 运算後取右边 16 位元字串 。

IV 计算

有上一篇的经验,IV计算就显得非常亲切,步骤如下:

  1. Nonce 值作 SHA256 加密
  2. 将英文转换成大写
  3. 取字串右边 16 码长度的部分,即为IV,
    就直接看程序吧!
sha256 = hashlib.sha256()
sha256.update(NonceValue.encode('utf-8'))
SHAValue = sha256.hexdigest().upper()
print(SHAValue)

IVValue=SHAValue[-16:]
print(IVValue)

结果

CB6FA68E42B655AB

内文加密(Message)

重头戏来罗,规格书显示:
将讯息内文以 AES CBC 方式加密,加密後的 Byte 以十六进制2位数字串相加。
https://ithelp.ithome.com.tw/upload/images/20210920/201409245vrTTeiG4P.png

因为对於实在是没有经验,直接针对关键字搜寻,下面是我最後参考的一篇文章,
Python AES/CBC/PKCS5Padding加解密
不过虽然使用以上程序,可以正常的加解密,但出来的加密内容,与范例不一样,因为不熟悉加解密的流程,看范例程序好像也没有太大头绪,大概是我PHP也不太会写,只好於线上搜寻相关问题了,
最後最後~突发奇想,想说找看看线上加密的网页,
参考如下
线上加解密网址
与网页上的程序结果一致,但不是我要的答案,这也是友人问我的问题,无法跟范例一致。
结果我发现线上加解密网站,多了一些选项,Output Text Format的选项,多了HEX选项,如下图,
绝大多数的线上范例,几乎都是base64的格式,
登登,果然改用HEX之後柳暗花明,终於找到答案了!!!!
https://ithelp.ithome.com.tw/upload/images/20210920/20140924CrQ1VsDXSA.png
规格书上若能说出是用HEX转码就省很多麻烦了~~
首先先安装以下套件

pip install pycryptodome 

程序如下

from Crypto.Cipher import AES


class AESCrypt:
    """
    AES/CBC/PKCS5Padding 加密
    """
    def __init__(self, key,iv):
        """
        使用金钥,加密模式进行初始化
        :param key:
        """
        if len(key) != 16 and len(key) !=32:
            raise RuntimeError('金钥长度非16位 and 32 位!!!')

        self.key = str.encode(key)
        self.iv = str.encode(iv)
        self.MODE = AES.MODE_CBC
        self.block_size = 16

        # 填充函数
        # self.padding = lambda data: data + (self.block_size - len(data) % self.block_size) * chr(self.block_size - len(data) % self.block_size)
        # 此处为一坑,需要现将data转换为byte再来做填充,否则中文特殊字元等会报错
        self.padding = lambda data: data + (self.block_size - len(data.encode('utf-8')) % self.block_size) * chr(self.block_size - len(data.encode('utf-8')) % self.block_size)
        # 截断函数
        self.unpadding = lambda data: data[:-ord(data[-1])]

    def aes_encrypt(self, plaintext):
        """
        加密
        :param plaintext: 明文
        :return:
        """
        try:
            # 填充16位
            padding_text = self.padding(plaintext).encode("utf-8")
            # 初始化加密器
            cryptor = AES.new(self.key, self.MODE, self.iv)
            # 进行AES加密
            encrypt_aes = cryptor.encrypt(padding_text)
            # 进行HEX转码
            encrypt_text = encrypt_aes.hex()
            # # 进行BASE64转码
            # encrypt_text = (base64.b64encode(encrypt_aes)).decode()
            return encrypt_text
        except Exception as e:
            logging.exception(e)

    def aes_decrypt(self, ciphertext):
        """
        解密
        :param ciphertext: 密文
        :return:
        """
        try:
            # 密文必须是16byte的整数倍
            # if len(ciphertext) % 16 != 0:
            #     raise binascii.Error('密文错误!')
            # print(ciphertext)
            cryptor = AES.new(self.key, self.MODE, self.iv)
            # 进行BASE64转码
            # plain_decode = base64.b64decode(ciphertext)

            # 进行HEX转码
            plain_decode = bytes.fromhex(ciphertext)
            # print(type(plain_decode)) #byte
            # 进行ASE解密
            decrypt_text = cryptor.decrypt(plain_decode)
            # 截取
            plain_text = self.unpadding(decrypt_text.decode("utf-8"))
            return plain_text
        except UnicodeDecodeError as e:
            logging.error('解密失败,请检查金钥是否正确!')
            logging.exception(e)
        except binascii.Error as e:
            logging.exception(e)
        except Exception as e:
            logging.exception(e)

if __name__ == '__main__':
    # 测试
    send_message_ori = {
    "ShopNo": "BA0026_001",
    "OrderNo": "A201804270001",
    "Amount": 50000,
    "CurrencyID": "TWD",
    "PayType": "A",
    "ATMParam": {"ExpireDate": "20180502"},
    "CardParam": {},
    "PrdtName": "虚拟帐号订单",
    "ReturnURL": "http://10.11.22.113:8803/QPay.ApiClient/Store/Return",
    "BackendURL": "http://10.11.22.113:8803/QPay.ApiClient/AutoPush/PushSuccess",
}
    cryptor = AESCrypt(hashID,IVValue)
    jsonText=json.dumps(send_message_ori, ensure_ascii=False).replace(' ', "")

    aes_encrypt_str = cryptor.aes_encrypt(jsonText).upper()
    print(f'加密结果为: {aes_encrypt_str}')
    aes_decrypt_str = cryptor.aes_decrypt(aes_encrypt_str)
    print(f'解密结果为: {aes_decrypt_str}')

结果如附

加密结果为: 2C236A4E91DB2F7670E79BBCE3A626EB728916919012681FF92BE0B4BBF57F5519AF1A469A1D8710B202CB2C2F3C12A770788D825AD0F0A22AED518545A0D244AD0F9C37C7C693EFFABE78B606BCDAED6284902F7F522BBA85D9BE7EFEF46C6793FB6A5D6624C2642A74EB312034BEA931EE3A5F3C660F3ABAA9032949AE86DEFEB452545807561D282C7B7C8E9102CED1404B8B542BC09CE12FA38F335BE7F027AE74BDDBADDB1790B172EFBF1FD25524E2BB64A626EA44643D4BD490E348E926BB7A48D5FA939EEC5BE681009E7AC7FED1C8475B715891321406960675B5A216032CF8657A3CB2B2D0C7FF85027D70E1F2B5DD414373912E97FA6FB85E9AB89B118BC545583CC9AC503F8BAD73C185CB97B28313618021F9217A30278043EF728BB5C49D231C4A22279864F68194254BC624789F36CCDEE75861CFC667CD8E9E89F1DB04ABA0D26FEF24BFE0470488
解密结果为: {"ShopNo":"BA0026_001","OrderNo":"A201804270001","Amount":50000,"CurrencyID":"TWD","PayType":"A","ATMParam":{"ExpireDate":"20180502"},"CardParam":{},"PrdtName":"虚拟帐号订单","ReturnURL":"http://10.11.22.113:8803/QPay.ApiClient/Store/Return","BackendURL":"http://10.11.22.113:8803/QPay.ApiClient/AutoPush/PushSuccess"}

加解密的部分都搞定了,剩下呼叫永丰API的部分了


<<:  Day6 - 下单函数使用,限价、市价、删单、改单怎麽写

>>:  从零开始学3D游戏设计:零件介面 Part.2 完成介面

微服务、容器化和无服务器(Microservices, Containerization, and Serverless)

微服务(Microservices) 微服务是一个低耦合的架构,可以通过以下方式实现重构一个单片应...

2.4.9 Design System - Input Checkbox/Radiobox

人生真的很奇妙 可能在某个时间轴曾经跟某个人的时间轴交错 但当下是不认识的 然後在几年过後又再次交...

Day8:EndPoint for Microsoft Defender 警示和事件

当我们布署完Microsoft Defender for Endpoint 接下来当侦测到威胁时,系...

[Day 09] 用HttpClientFactory实践WebAPI服务 - 升级永丰API至.Net Core

如果有从永丰银行API那边下载过C#版的丰收款QPay.SampleCode,应该能发现他们目前的专...

Day 30 Elastic Cloud挑战结束心得

Day 30 Elastic Cloud挑战结束心得 前言 三十天其实一下子就过去了,这次比赛让我重...