当我们在测试支付接口的时候,发现每个接口需要将请求的敏感数据加密后才能请求接口,遇到这种问题怎么解决呢?
特别是当我们在写python接口自动化测试脚本的时候,这是必须要解决的问题。下面我来详细的写下这个过程:
首先,我们需要拿到商户的公钥和公司的公钥,商户有自己的私钥,私钥只要商户自己知道。加密的时候拿公司的公钥加密、签名时拿商户的私钥签名。 这样传送的数据会拿商户的公钥去验签才能通过。
其次,注意python和Java的加密算法要统一, 通常使用的是SHA1安全散列算法操作的。
下面上代码:
假设我们拿到了公钥和私钥,定义参数为:
public_key=''
private_key=''
要加密请求参数为:
reqData='{"tranAmt":"0.50","payType":"2","cardNo":"1234561121456789356","holderName":"王二三",' \
'"cardAvailableDate":"","cvv2":"","mobileNo":"18900001001","identityType":"01","identityCode":"130102196303250459","bizProtocolNo":"","payProtocolNo":"","frontUrl":"","notifyUrl":"notifyUrl.do","orderExpireTime": "","merUserId":"merUserId_'+now+'","merUserIp":"106.37.175.130","riskExpand":"","goodsInfo":"测试","subMerchantId":""}'
我们发现这段请求参数特别长,把参数定义为byte类型,方便我们在加密时使用。长字符串加密时,需要把参数分割后再进行加密。方法如下:
# 公钥加密---字符串
def rsa_long_encrypt(pub_key_str, msg):
msg = msg.encode('utf-8') #utf-8
length = len(msg)
default_length = 117
#公钥加密
pubobj = Cipher_pkcs1_v1_5.new(RSA.importKey(pub_key_str))
#长度不用分段
if length < default_length:
return base64.b64encode(pubobj.encrypt(msg))
#需要分段
offset = 0
res = []
while length - offset > 0:
if length - offset > default_length:
res.append(pubobj.encrypt(msg[offset:offset+default_length]))
else:
res.append(pubobj.encrypt(msg[offset:]))
offset += default_length
byte_data = b''.join(res)
return base64.b64encode(byte_data)
with open('公司的公钥/CompPublicKey.pem') as f:
'''读取公钥并加密'''
key = f.read()
result = rsa_long_encrypt(key, reqData)
reqEncryStr=result.decode('utf-8')
加密后需要转码才能使用,转为通用的utf-8格式。
这样数据加密就完成了。下面我们看签名:
假设我们的签名串格式顺序是这样的:
reqStr= 'version=[2.0]tranCode=[EXP12]merId=[]merOrderId=[EXP12_'+now+']submitTime=['+now+']msgCiphertext=['+reqEncryStr+']'
#私钥签名方法
def requestSign():
key_bytes = bytes(private_key, encoding="utf-8")
key_bytes = b64decode(key_bytes)
key = RSA.importKey(key_bytes)
hash_value = SHA1.new(bytes(reqStr, encoding="utf-8")) #origin_data
signer = PKCS1_v1_5.new(key)
signature = signer.sign(hash_value)
return b64encode(signature)
签名使用的是SHA1方法创建新的加密串,解码的时候才能正确解析。
接口请求参数为:
requestData = { # 请求头参数
"charset": "1", # 固定 UTF-8
"submitTime": now, # 请求提交时间
"signType": "1", # 签名类型-固定RSA:1
"merId": '11000003032',
"tranCode": "EXP12", # 交易代码
"merAttach": "",
"version": "2.0",
"signValue": signReqStr, # merSignStr 生成签名- 验签- 商户签名类密文串
"msgCiphertext": reqEncryStr, #
"merOrderId": "EXP12_" + now #
}
resp = requests.post(url=url, data=requestData )
这样完整的加密,到请求就完成了。