### 数字签名
(1)为了保证数据传输过程中的数据真实性和完整性,我们需要对数据进行数字签名,在接收签名数据之后进行签名校验。
(2)签名有两个步骤,先按一定规则拼接要签名的原始串,再选择具体的算法和密钥计算出签名结果。
(3)原始串中,字段名和字段值都采用原始值,不进行URL Encode。
(4)平台返回的应答或通知消息可能会由于升级增加参数,请验证应答签名时注意允许这种情况。
### 签名方法
(1)设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1\&key2=value2…)拼接成字符串stringA。
(2)在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
### 签名样例
(1)假设需要传递的参数如下
```
array:12 [▼
"order_id" => "PT2307041351078661"
"oil_type" => "92#"
"oil_gun" => "1号枪"
"oil_price" => "6.25"
"oil_volume" => "56"
"order_total" => "350"
"order_time" => "2023-07-04 13:51:07"
"card_no" => ""
"appid" => "230703147355731"
"station_number" => "OP12335566"
"brand" => "zx001"
"nonce_str" => "64a3b34bda295"
]
```
(2)对参数按照key=value的格式,并按照参数名ASCII字典序排序如下
```
appid=230703147355731&brand=zx001&nonce_str=64a3b34bda295&oil_gun=1号枪&oil_price=6.25&oil_type=92#&oil_volume=56&order_id=PT2307041351078661&order_time=2023-07-04 13:51:07&order_total=350&station_number=OP12335566
```
(3)拼接秘钥key (同AppSecret)
```
appid=230703147355731&brand=zx001&nonce_str=64a3b34bda295&oil_gun=1号枪&oil_price=6.25&oil_type=92#&oil_volume=56&order_id=PT2307041351078661&order_time=2023-07-04 13:51:07&order_total=350&station_number=OP12335566&key=019fa2de62ee14771ea8b76820e8dc18
```
4)计算签名
```
签名结果: 58DF44E3766423064265B0332D45BE19
MD5计算:sign=MD5(stringSignTemp).toUpperCase()
```
**注:md5加密为32位 且 大写。除sign字段外,其他参数按照签名方法验签,空值不传递,不参与签名组串。**
*****
## 签名方法demo:
PHP:
```php
function calculateSign($data, $key) {
// 去除空值参数和sign字段
$filteredData = array_filter($data, function($value, $key) {
return $value !== '' && strtolower($key) !== 'sign';
}, ARRAY_FILTER_USE_BOTH);
// 将参数按照参数名ASCII码从小到大排序
ksort($filteredData);
// 使用URL键值对的格式拼接参数
$stringA = '';
foreach ($filteredData as $k => $v) {
$stringA .= $k . '=' . $v . '&';
}
$stringA .= 'key=' . $key;
// 对拼接后的字符串进行MD5运算,并转换为大写
$signValue = strtoupper(md5($stringA));
return $signValue;
}
```
Java:
```java
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SignCalculator {
public static String calculateSign(Map<String, String> data, String key) throws NoSuchAlgorithmException {
// 去除空值参数和sign字段
Map<String, String> filteredData = new HashMap<>();
for (Map.Entry<String, String> entry : data.entrySet()) {
if (!entry.getValue().isEmpty() && !entry.getKey().equalsIgnoreCase("sign")) {
filteredData.put(entry.getKey(), entry.getValue());
}
}
// 将参数按照参数名ASCII码从小到大排序
List<String> sortedKeys = new ArrayList<>(filteredData.keySet());
Collections.sort(sortedKeys);
// 使用URL键值对的格式拼接参数
StringBuilder stringA = new StringBuilder();
for (String k : sortedKeys) {
stringA.append(k).append("=").append(filteredData.get(k)).append("&");
}
stringA.append("key=").append(key);
// 对拼接后的字符串进行MD5运算,并转换为大写
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] digest = md5.digest(stringA.toString().getBytes());
StringBuilder signValue = new StringBuilder();
for (byte b : digest) {
signValue.append(String.format("%02x", b & 0xff));
}
return signValue.toString().toUpperCase();
}
public static void main(String[] args) throws NoSuchAlgorithmException {
// 示例用法
Map<String, String> data = new HashMap<>();
data.put("sign", "xxxxxx");
data.put("key1", "value1");
data.put("key2", "");
data.put("", "value3");
String key = "your_key";
String signValue = calculateSign(data, key);
System.out.println(signValue);
}
}
```
C#
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class SignCalculator
{
public static string CalculateSign(Dictionary<string, string> data, string key)
{
// 去除空值参数和sign字段
var filteredData = data.Where(x => !string.IsNullOrEmpty(x.Value) && x.Key.ToLower() != "sign")
.ToDictionary(x => x.Key, x => x.Value);
// 将参数按照参数名ASCII码从小到大排序
var sortedData = filteredData.OrderBy(x => x.Key);
// 使用URL键值对格式拼接参数
StringBuilder stringA = new StringBuilder();
foreach (var pair in sortedData)
{
stringA.Append(pair.Key).Append("=").Append(pair.Value).Append("&");
}
stringA.Append("key=").Append(key);
// 对拼接后的字符串进行MD5运算,并转换为大写
using (var md5 = MD5.Create())
{
byte[] bytes = Encoding.UTF8.GetBytes(stringA.ToString());
byte[] hashBytes = md5.ComputeHash(bytes);
var signValue = new StringBuilder();
foreach (byte b in hashBytes)
{
signValue.Append(b.ToString("x2"));
}
return signValue.ToString().ToUpper();
}
}
public static void Main(string[] args)
{
// 示例用法
Dictionary<string, string> data = new Dictionary<string, string>
{
{ "sign", "xxxxxx" },
{ "key1", "value1" },
{ "key2", "" },
{ "", "value3" }
};
string key = "your_key";
string signValue = CalculateSign(data, key);
Console.WriteLine(signValue);
}
}
```
Python:
```python
import hashlib
from typing import Dict
def calculate_sign(data: Dict[str, str], key: str) -> str:
# 去除空值参数和sign字段
filtered_data = {k: v for k, v in data.items() if v and k.lower() != "sign"}
# 将参数按照参数名ASCII码从小到大排序
sorted_keys = sorted(filtered_data.keys())
# 使用URL键值对的格式拼接参数
string_a = "&".join(f"{k}={filtered_data[k]}" for k in sorted_keys)
string_a += f"&key={key}"
# 对拼接后的字符串进行MD5运算,并转换为大写
sign_value = hashlib.md5(string_a.encode("utf-8")).hexdigest().upper()
return sign_value
# 示例用法
data = {
"sign": "xxxxxx",
"key1": "value1",
"key2": "",
"": "value3"
}
key = "your_key"
sign_value = calculate_sign(data, key)
print(sign_value)
```
Golang:
```go
package main
import (
"crypto/md5"
"encoding/hex"
"sort"
"strings"
)
func calculateSign(data map[string]string, key string) string {
// 去除空值参数和sign字段
filteredData := make(map[string]string)
for k, v := range data {
if v != "" && strings.ToLower(k) != "sign" {
filteredData[k] = v
}
}
// 将参数按照参数名ASCII码从小到大排序
var sortedKeys []string
for k := range filteredData {
sortedKeys = append(sortedKeys, k)
}
sort.Strings(sortedKeys)
// 使用URL键值对的格式拼接参数
var stringA string
for _, k := range sortedKeys {
stringA += k + "=" + filteredData[k] + "&"
}
stringA += "key=" + key
// 对拼接后的字符串进行MD5运算,并转换为大写
hash := md5.Sum([]byte(stringA))
signValue := hex.EncodeToString(hash[:])
return strings.ToUpper(signValue)
}
func main() {
// 示例用法
data := map[string]string{
"sign": "xxxxxx",
"key1": "value1",
"key2": "",
"": "value3",
}
key := "your_key"
signValue := calculateSign(data, key)
println(signValue)
}
```
C++:
```cpp
#include <iostream>
#include <map>
#include <algorithm>
#include <cctype>
#include <iomanip>
#include <sstream>
#include <openssl/md5.h>
std::string calculateSign(const std::map<std::string, std::string>& data, const std::string& key) {
// 去除空值参数和sign字段
std::map<std::string, std::string> filteredData;
for (const auto& pair : data) {
if (!pair.second.empty() && pair.first != "sign") {
filteredData[pair.first] = pair.second;
}
}
// 将参数按照参数名ASCII码从小到大排序
std::vector<std::string> sortedKeys;
for (const auto& pair : filteredData) {
sortedKeys.push_back(pair.first);
}
std::sort(sortedKeys.begin(), sortedKeys.end());
// 使用URL键值对的格式拼接参数
std::stringstream stringAStream;
for (const auto& key : sortedKeys) {
stringAStream << key << "=" << filteredData[key] << "&";
}
stringAStream << "key=" << key;
std::string stringA = stringAStream.str();
// 对拼接后的字符串进行MD5运算,并转换为大写
unsigned char hash[MD5_DIGEST_LENGTH];
MD5(reinterpret_cast<const unsigned char*>(stringA.c_str()), stringA.size(), hash);
std::stringstream signValueStream;
signValueStream << std::hex << std::uppercase << std::setfill('0');
for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
signValueStream << std::setw(2) << static_cast<unsigned>(hash[i]);
}
std::string signValue = signValueStream.str();
return signValue;
```
JavaScript:
```
function calculateSign(data, key) {
// 去除空值参数和sign字段
const filteredData = {};
for (const [k, v] of Object.entries(data)) {
if (v && k.toLowerCase() !== 'sign') {
filteredData[k] = v;
}
}
// 将参数按照参数名ASCII码从小到大排序
const sortedKeys = Object.keys(filteredData).sort();
// 使用URL键值对的格式拼接参数
let stringA = sortedKeys.map(k => `${k}=${filteredData[k]}`).join('&');
stringA += `&key=${key}`;
// 对拼接后的字符串进行MD5运算,并转换为大写
const md5 = require('crypto').createHash('md5');
const signValue = md5.update(stringA).digest('hex').toUpperCase();
return signValue;
}
// 示例用法
const data = {
sign: 'xxxxxx',
key1: 'value1',
key2: '',
'': 'value3',
};
const key = 'your_key';
const signValue = calculateSign(data, key);
console.log(signValue);
```