Body模式接口签名
这里以RSA签名为例,国密更换对应的加密方式即可
1、怎么判断当前请求的API是否属于请求参数里带Body参数?
你可以点击查看一个API接口说明文档,可以看到该接口请求参数部分参数类型为Body参数,不同的参数类型构造请求签名略有不同,Body参数对应的是Http POST请求方式,将参数放在Body体里面发送请求.
为了方便大家理解,我们将使用一对测试的公私钥,通过命令行形式演示接口调用签名过程,一步一步介绍请求参数里带body参数,如何计算签名。强烈建议你使用测试的公私钥,按照本文实际操作一下,如果算出来的签名和文档的一致则说明计算无误。实际业务中请替换为你的真实的参数。
假设你的body参数如下所示,我们将一步步演示如何计算签名
out_trade_no = "1766037897757"
acc_info = "张三|18888888888"
notify_url = "https://www.baofu.com/"
amount = {"total":100,"txn":90}
2. 如何构造请求签名
2.1. 获取商户API证书
此处我们已经拿到了商户API证书,并且将对应的商户API私钥api_key.pem,保存在了本地
商户号:10283021
证书序列号:139248429
宝付证书序列号: 138924182
API证书私钥:你需要将以下文件保存为pem格式,为了避免大家和自己的真实的公私钥混淆,此处将其保存为了api_test_key.pem
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCm2mb6q8gMKH/3
CNTbpJAIrbqiBiQGEOtjGcBrDYltsGynWgNscqT7WvfzU14FQbYcQUC5T4Wvva7m
i3fIp3OgX8VqMDNA0qebnr38Pe6kqiLyZgFpJPXlSKDyPyqhRbVTbXssvSMQeVKc
dXeVxoNNeoOlNFHgF/P0io6AmAVnz+hN8SiZKuOsth5/zUTLGvtkgxBcQooQrtXh
RcpLT798OyIb9xeJ2HO3xRtMv2+perEzb4gMibI74UBz+2QEbnkubPE+2jU2rRZu
dnNEz/BPOt3Qj/w2V6/G0VumGDh6+UeMU0jv4aupHztWITC4Akn0l7lBCNy3lgl8
VFaJnkIxAgMBAAECggEAYGL8aESB7NwciDWW2UdoWUsa7GxFtSdjAz2mFXGdeTsY
mVh7b9OOkRGM+Qio4LqEHDBp1mMk5E/cUJwy1zw8pGGO5nfvs7u9TT3XnHaefIs4
YvUgTYAneIuLRkXNN5rQU+CD7mVYczTSz0Vgjqo9wa1LjUz7G0xbBmJgTdMEFGJs
eJjy6AbJo0CGIwp6HJbTm4CmOUgXnnDAIbEGTIRImkZFH/rzneIeR7oZ77FVwxr1
CZB2gfRCov/yRPbw8vnryYkmvQ7D/ze3j5097vRg/MoDGBSdoOwcmo75vyofr0AS
zytMjmHYyifqkf5slPropSiJeGf4p/7gtKyF6dE/XQKBgQDVAlJ+4U5ZVGOuDc3+
sAhz8CTzgFNlq9vKuSoFK6hOz2L+cwj+E7NXGkOe2DsHHZNy2Xqxk7caKhPEp1z9
hhpMpyLVMoFt6CKemyoRBWDCQwLLwem9SZF/IAyovBkLiH36P42Jm26gUkNMKC/5
Zhtqxf6RZgRQzbVudJi47vIRCwKBgQDIh0+v27Oo+DM3fhObH4I1NrXpWOEGH7OQ
G1dEsMuFYF4hjGhg0kBEP3w9vVdl2+mRllZKTsx9oqjb8OibPLLIH8xsdbAB0WLf
JvjLu4wl/ILUzN1RI03dWnnv2EnEeQn6c3hizvrJ9wR5U4ue9RPVnQooJ0hZF1PU
uCL5fWK3MwKBgElReU/PAYbh80WP3t3Rfbdaa32dKBeQ5iCLR5lsA4zM+YgX1HqQ
EWTj126vgvHaDkyz6vWAoL/Sx+cirHFfXWIRDX5Q2hgYlQH+6qXdMgbrxeSYpHnQ
/tHBGFpkFELSAnrGsVMyOwvYBO4LzyeLK9i+ufcWJFoj1FVmsMLHDG8tAoGARdbi
iQQCoYG4DMarO2aQ6cmhN6EN1h0qY7EyBqlwaIZ0okiNfdMcMOjPc41DKCWcRmlO
qlihXcxN9TQFPzO3rH1urAOdBjUPs1qWYhZyrDQyuLyVBBJApyxAtajloDjrob+f
mQIvVDHk7ACN6xG+E7K6+9salnTKbJapD618uQMCgYBNy6XUvzLkP/A1U/UZdtcx
l8GwU/dturLxz4CyGbqDw4ubaYY2e13lnqHUqQgPtiSyH51nq3tdo8G0YAJdfkSv
KvnfslW91fyEBUKnkdW1o3/1UFU/wprZ7ixVL/F42A4xDu7OFE8EnweJOZ0jWceE
OdhCkaIGBCfRnlECRK8UyQ==
-----END PRIVATE KEY-----
假设我们已经获取了宝付公钥并保存为 139129421.pem。内容如下:
$ cat 139129421.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiA1Mb2lh1CcYiC6TNNJ+
AF6hM0gSkACy6axeGzTD6t5JMvAhyoHTbXeQM2t2frrxO4iIrT5uFFbgG+iJtuzO
xXxEPgLgv4ytZz8vLONUWVrbJbwYZY4pUhqyN5q67kYQQ+zB1+FMtVpheR2x5fJF
b8yGj8odpQssHOfJoyT0vnCIebhWUzcg03OU7FML6q22JRdIGO6uubq6rKpgNaly
oBxzEqUVxN+5+1bm22H5g4aWQTEqRmGqmrIo/CN0WSUhdj72dAj0L5YkscHkyWXZ
3zMLFeprSjW86pj2h7iM/0/bl9COJJo7t38a1SURrZpXktoWj/rP++Po4mEQBGfF
aQIDAQAB
-----END PUBLIC KEY-----
2.2. 构造签名串
签名串一共有六行,每一行为一个参数。结尾以\n(换行符,ASCII编码值为0x0A)结束,包括最后一行。如果参数本身以\n结束,也需要附加一个\n。
HTTP请求方法\n
URL\n
请求时间戳\n
请求随机串\n
数字信封\n
请求报文主体\n
第一步,获取HTTP请求的方法
POST
第二步,获取请求的绝对URL(请注意需要去除域名部分)。假设完整请求URL为: https://api.baofu.com/v4/test/pagequery ,则绝对URL为
/v4/test/pay
第三步,获取发起请求时的系统当前时间戳,即格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数,作为请求时间戳。宝付会拒绝处理很久之前发起的请求,请商户保持自身系统的时间准确。
$ date +%s
1766038862
第四步,生成一个请求随机串,这里,我们使用命令行直接生成一个。
$ openssl rand -hex 16 | tr -d '\n' | tr 'a-f' 'A-F'
5FC2FC21E871BD5B8C7A3E0B0339BC8F
第五步,生成数字信封。
body参数中acc_info是敏感信息,需要数字信封对其进行加密,数字信封的使用请参考【敏感字段加解密说明】
生成数字信封,要求16位字符串
$ openssl rand -hex 8
667015563483cbfd
将该字符串转换为十六进制,保存为key_hex.txt,方便后续使用
$ echo -n "667015563483cbfd" | xxd -p > key_hex.txt
echo "密钥十六进制值:$KEY_HEX"
密钥十六进制值:36363730313535363334383363626664
加密:
BLOCK_SIZE=16
KEY_HEX=$(tr -d '\n' < key_hex.txt)
PLAINTEXT="张三|18888888888"
TRIMMED_PLAINTEXT=$(echo -n "$PLAINTEXT" | tr -d '[:space:]')
echo -n "$TRIMMED_PLAINTEXT" > /tmp/tmp_plain.bin
FILE_SIZE=$(stat -c %s /tmp/tmp_plain.bin)
if [ $((FILE_SIZE % BLOCK_SIZE)) -ne 0 ]; then
PAD_SIZE=$((BLOCK_SIZE - (FILE_SIZE % BLOCK_SIZE)))
dd if=/dev/zero bs=1 count=$PAD_SIZE >> /tmp/tmp_plain.bin 2>/dev/null
fi
CIPHER_HEX=$(openssl enc -e -aes-128-cbc -nopad -in /tmp/tmp_plain.bin -K $KEY_HEX -iv $KEY_HEX | xxd -p -c 256 | tr [:lower:] [:upper:])
rm -f /tmp/tmp_plain.bin
echo "加密完成!"
echo "16进制密文(全大写):$CIPHER_HEX"
A4D34D538B79C87A405194B8C5CA2E98D23EB7B2039E33FFA2705F44DE2AB497
使用宝付公钥对数字信封进行加密
echo -n "667015563483cbfd" | openssl pkeyutl -encrypt -pubin -inkey 139129421.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha1 -pkeyopt rsa_mgf1_md:sha1 | base64
Oe4wZgt2QvrLhDPWtudWRbgXiOOxJ7d6ri48BfDOpSx8tKTP8aTclDbDp+GXDXZ1yS4LFVbr8yN7
wVZfgPJC5J5/J9sgfVGMmWc2ZP6VwcWZvfuKhr7A1evKfG9xEG0HtUMqlNtwB5yIjkwP04ZeGniD
Pf2ylk4e6E5RYmKnhhmcli7wxZbshChyBzycHzOH5AX6X1U3HU8uMsXGXZtvzPS1VTNp+45tDWwy
sjOs0Wp8K3T/vAyumqW/Rzz4iwSP30n4Z2xYZJa5sic48G0/2Uqg2U8A8Z3EudA9q73KgiaFHpJ2
DQXxg9PWC7Ay55gOenvDjopPGuTVR5psl3JMNw==
第六步,获取请求中的请求报文主体(request body)。
你可以将所有参数放在一行,对应的发起签名请求的时候body参数也应该放在一行。
你也可以将参数以多行计算签名,对应发起请求的时候body参数也要是多行。即你计算签名时body是怎么样的,你发起请求时body就应该是怎么样的。
这里以将所有参数放到一行做演示
{"amount":{"total":100,"txn":90},"out_trade_no":"1766037897757","acc_info":"A4D34D538B79C87A405194B8C5CA2E98D23EB7B2039E33FFA2705F44DE2AB497","notify_url":"https://www.baofu.com/"}
第七步,按照前述规则,构造的请求签名串如下:
POST\n
/v4/test/pay\n
1766038862\n
5FC2FC21E871BD5B8C7A3E0B0339BC8F\n
Oe4wZgt2QvrLhDPWtudWRbgXiOOxJ7d6ri48BfDOpSx8tKTP8aTclDbDp+GXDXZ1yS4LFVbr8yN7wVZfgPJC5J5/J9sgfVGMmWc2ZP6VwcWZvfuKhr7A1evKfG9xEG0HtUMqlNtwB5yIjkwP04ZeGniDPf2ylk4e6E5RYmKnhhmcli7wxZbshChyBzycHzOH5AX6X1U3HU8uMsXGXZtvzPS1VTNp+45tDWwysjOs0Wp8K3T/vAyumqW/Rzz4iwSP30n4Z2xYZJa5sic48G0/2Uqg2U8A8Z3EudA9q73KgiaFHpJ2DQXxg9PWC7Ay55gOenvDjopPGuTVR5psl3JMNw==\n
{"amount":{"total":100,"txn":90},"out_trade_no":"1766037897757","acc_info":"A4D34D538B79C87A405194B8C5CA2E98D23EB7B2039E33FFA2705F44DE2AB497","notify_url":"https://www.baofu.com/"}\n
注意:当请求报文主体或者数字信封是一个空串时,只需要附加\n即可
2.3. 计算签名值
绝大多数编程语言提供的签名函数支持对签名数据进行签名。强烈建议商户调用该类函数,使用商户私钥对待签名串进行SHA256 with RSA签名,并对签名结果进行16进制编码转大写得到签名值。
请注意处理单双引号转义问题,第二行的外层是单引号,则里面的参数不需要转义,如果第二行最外层使用了双引号,则body参数的双引号都需要转义
$ echo -n -e \ 'POST\n/v4/test/pay\n1766038862\n5FC2FC21E871BD5B8C7A3E0B0339BC8F\nOe4wZgt2QvrLhDPWtudWRbgXiOOxJ7d6ri48BfDOpSx8tKTP8aTclDbDp+GXDXZ1yS4LFVbr8yN7wVZfgPJC5J5/J9sgfVGMmWc2ZP6VwcWZvfuKhr7A1evKfG9xEG0HtUMqlNtwB5yIjkwP04ZeGniDPf2ylk4e6E5RYmKnhhmcli7wxZbshChyBzycHzOH5AX6X1U3HU8uMsXGXZtvzPS1VTNp+45tDWwysjOs0Wp8K3T/vAyumqW/Rzz4iwSP30n4Z2xYZJa5sic48G0/2Uqg2U8A8Z3EudA9q73KgiaFHpJ2DQXxg9PWC7Ay55gOenvDjopPGuTVR5psl3JMNw==\n{"amount":{"total":100,"txn":90},"out_trade_no":"1766037897757","acc_info":"A4D34D538B79C87A405194B8C5CA2E98D23EB7B2039E33FFA2705F44DE2AB497","notify_url":"https://www.baofu.com/"}\n' \
| openssl dgst -sha256 -sign api_test_key.pem \
| xxd -p | tr -d '\n' | tr 'a-f' 'A-F'
得出的签名值如下,你可以用一些校验工具校验你通过命令行或者你的代码得到的签名值是否和以下值一样,一样则表示计算过程无误,如果不一致则请检查签名的参数是否一致、签名串是否有严格换行等
3ED36FF64CB42A3D8F91C79CFC11CCD62ACC2A021BB18A7D9DCBC3E75A1BB091E6802BA4049AFA20DC826942474268A08B7392B768EEFD97CD8AA8A4588FC32D71138E89C9EC34F60BF67EAF17E42E04E2E21ABC4341ED7A5730848E9D55D0B0F683B2BAA300E9DB6C8FF2DDD25FAF16DFA93609BC5F34704736791C153AE4055C75F558C6D247A498F23F12DC38F472108BD1BA50AF6911B2BBDB05E307D056C80C4A3B03DE79424BFD649226730CB974226BCAB165EF629D25E79CEB7D3675C9090DD02EEA4190FCA6B4FE020EFC9C6623845A38C67C3125065568D858D27B52F3D403F3A2B5DA6F636615B8ADE354A90434A9A4EA8AD6800DD12175C8CED6
2.4. 设置HTTP头
宝付要求请求通过HTTP Authorization头来传递签名。Authorization由认证类型和签名信息两个部分组成。
Authorization: 认证类型 签名信息
具体组成为:
- 认证类型,取值 RSA/SM2
签名信息
| 参数名 | 说明 |
|---|---|
| mch_id | 宝付分配的唯一商编 |
| app_id | 宝付终端号,选填,若传值,则使用宝付4.0证书 |
| nonce_str | 请求随机串,和上面构造签名串的随机串要保持一致 |
| timestamp | 时间戳 和上面构造签名串的时间戳要保持一致 |
| serial_no | 商户API证书序列号,用于声明所使用的证书 |
| baofu_serial_no | 宝付证书序列号, 联系技术支持提供 |
| dgtl_envlp | 数字信封(非必填),16位密钥,用宝付公钥加密后传输 |
| signature | 签名值,上面算出来的签名值 |
注意:以上七项签名信息,无顺序要求。数字信封参数可以为空
根据示例最终生成的Authrization头如下:
Authorization: RSA mch_id="10283021",app_id="120838",nonce_str="5FC2FC21E871BD5B8C7A3E0B0339BC8F",signature="3ED36FF64CB42A3D8F91C79CFC11CCD62ACC2A021BB18A7D9DCBC3E75A1BB091E6802BA4049AFA20DC826942474268A08B7392B768EEFD97CD8AA8A4588FC32D71138E89C9EC34F60BF67EAF17E42E04E2E21ABC4341ED7A5730848E9D55D0B0F683B2BAA300E9DB6C8FF2DDD25FAF16DFA93609BC5F34704736791C153AE4055C75F558C6D247A498F23F12DC38F472108BD1BA50AF6911B2BBDB05E307D056C80C4A3B03DE79424BFD649226730CB974226BCAB165EF629D25E79CEB7D3675C9090DD02EEA4190FCA6B4FE020EFC9C6623845A38C67C3125065568D858D27B52F3D403F3A2B5DA6F636615B8ADE354A90434A9A4EA8AD6800DD12175C8CED6",timestamp="1766038862",serial_no="139248429",baofu_serial_no="138924182"
最终我们可以组一个包含了签名的HTTP请求了。请注意这里因为HTTPS请求里面带有&字符,因此需要加上双引号
$ curl -X POST \
https://api.baofu.com/v4/test/pay \
-H 'Authorization: RSA mch_id="10283021",nonce_str="5FC2FC21E871BD5B8C7A3E0B0339BC8F",signature="3ED36FF64CB42A3D8F91C79CFC11CCD62ACC2A021BB18A7D9DCBC3E75A1BB091E6802BA4049AFA20DC826942474268A08B7392B768EEFD97CD8AA8A4588FC32D71138E89C9EC34F60BF67EAF17E42E04E2E21ABC4341ED7A5730848E9D55D0B0F683B2BAA300E9DB6C8FF2DDD25FAF16DFA93609BC5F34704736791C153AE4055C75F558C6D247A498F23F12DC38F472108BD1BA50AF6911B2BBDB05E307D056C80C4A3B03DE79424BFD649226730CB974226BCAB165EF629D25E79CEB7D3675C9090DD02EEA4190FCA6B4FE020EFC9C6623845A38C67C3125065568D858D27B52F3D403F3A2B5DA6F636615B8ADE354A90434A9A4EA8AD6800DD12175C8CED6",timestamp="1766038862",serial_no="139248429",baofu_serial_no="138924182", dgtl_envlp ="Oe4wZgt2QvrLhDPWtudWRbgXiOOxJ7d6ri48BfDOpSx8tKTP8aTclDbDp+GXDXZ1yS4LFVbr8yN7wVZfgPJC5J5/J9sgfVGMmWc2ZP6VwcWZvfuKhr7A1evKfG9xEG0HtUMqlNtwB5yIjkwP04ZeGniDPf2ylk4e6E5RYmKnhhmcli7wxZbshChyBzycHzOH5AX6X1U3HU8uMsXGXZtvzPS1VTNp+45tDWwysjOs0Wp8K3T/vAyumqW/Rzz4iwSP30n4Z2xYZJa5sic48G0/2Uqg2U8A8Z3EudA9q73KgiaFHpJ2DQXxg9PWC7Ay55gOenvDjopPGuTVR5psl3JMNw=="' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"amount":{"total":100,"txn":90},"out_trade_no":"1766037897757","acc_info":"A4D34D538B79C87A405194B8C5CA2E98D23EB7B2039E33FFA2705F44DE2AB497","notify_url":"https://www.baofu.com/"}'