本文目录导读:

我来为您创建一个完整的PHP签名工具类,包含常见的签名算法实现:
<?php
/**
* 签名工具类
* 支持 MD5、SHA1、SHA256、HMAC 等常见签名算法
*/
class SignUtil
{
/**
* 签名算法常量
*/
const ALG_MD5 = 'md5';
const ALG_SHA1 = 'sha1';
const ALG_SHA256 = 'sha256';
const ALG_HMAC_MD5 = 'hmac_md5';
const ALG_HMAC_SHA1 = 'hmac_sha1';
const ALG_HMAC_SHA256 = 'hmac_sha256';
const ALG_RSA = 'rsa';
const ALG_ECDSA = 'ecdsa';
/**
* 生成签名
*
* @param array $params 待签名参数
* @param string $secret 密钥
* @param string $algorithm 签名算法
* @param array $options 额外选项
* @return string 签名结果
*/
public static function sign(array $params, string $secret, string $algorithm = self::ALG_MD5, array $options = []): string
{
// 1. 过滤空值和非签名参数
$params = self::filterParams($params, $options['exclude_keys'] ?? []);
// 2. 排序参数
ksort($params);
// 3. 拼接字符串
$str = self::buildSignStr($params, $options['separator'] ?? '&');
// 4. 根据算法生成签名
switch ($algorithm) {
case self::ALG_MD5:
return self::md5Sign($str, $secret);
case self::ALG_SHA1:
return self::sha1Sign($str, $secret);
case self::ALG_SHA256:
return self::sha256Sign($str, $secret);
case self::ALG_HMAC_MD5:
return self::hmacSign($str, $secret, 'md5');
case self::ALG_HMAC_SHA1:
return self::hmacSign($str, $secret, 'sha1');
case self::ALG_HMAC_SHA256:
return self::hmacSign($str, $secret, 'sha256');
case self::ALG_RSA:
return self::rsaSign($str, $secret, $options['private_key'] ?? null);
case self::ALG_ECDSA:
return self::ecdsaSign($str, $secret, $options['private_key'] ?? null);
default:
throw new InvalidArgumentException("不支持的签名算法: {$algorithm}");
}
}
/**
* 验证签名
*
* @param array $params 请求参数(包含签名)
* @param string $secret 密钥
* @param string $algorithm 签名算法
* @param string $signName 签名参数名
* @param array $options 额外选项
* @return bool
*/
public static function verify(array $params, string $secret, string $algorithm = self::ALG_MD5, string $signName = 'sign', array $options = []): bool
{
if (!isset($params[$signName])) {
throw new InvalidArgumentException("缺少签名参数: {$signName}");
}
$clientSign = $params[$signName];
// 复制参数并移除签名字段
$params = $params;
unset($params[$signName]);
// 生成服务端签名
$serverSign = self::sign($params, $secret, $algorithm, $options);
// 使用 hash_equals 防止时序攻击
return hash_equals($clientSign, $serverSign);
}
/**
* MD5 签名(微信支付风格)
*
* @param array $params 参数
* @param string $key 密钥
* @return string
*/
public static function wechatSign(array $params, string $key): string
{
// 过滤空值和sign
$params = self::filterParams($params, ['sign', 'sign_type']);
// 排序
ksort($params);
// 拼接
$str = self::buildSignStr($params);
$str .= '&key=' . $key;
return strtoupper(md5($str));
}
/**
* 阿里云签名(RPC风格)
*
* @param array $params 参数
* @param string $secret 密钥
* @return string
*/
public static function aliyunRpcSign(array $params, string $secret): string
{
// 排序并过滤
ksort($params);
$params = self::filterParams($params, ['Signature', 'signature']);
// 构造规范请求
$str = '';
foreach ($params as $key => $value) {
$str .= '&' . self::percentEncode($key) . '=' . self::percentEncode($value);
}
$str = substr($str, 1);
$stringToSign = 'GET&%2F&' . self::percentEncode($str);
return base64_encode(hash_hmac('sha1', $stringToSign, $secret . '&', true));
}
/**
* 腾讯云签名(TC3-HMAC-SHA256)
*
* @param array $params 参数
* @param string $secretId 密钥ID
* @param string $secretKey 密钥
* @param array $options 选项
* @return string
*/
public static function tencentCloudSign(array $params, string $secretId, string $secretKey, array $options = []): string
{
$timestamp = $options['timestamp'] ?? time();
$date = gmdate('Y-m-d', $timestamp);
$service = $options['service'] ?? '';
$host = $options['host'] ?? '';
$action = $options['action'] ?? '';
$version = $options['version'] ?? '';
$region = $options['region'] ?? '';
$method = $options['method'] ?? 'POST';
$payload = $options['payload'] ?? json_encode($params);
// 步骤1:拼接规范请求串
$payloadHash = hash('sha256', $payload);
$canonicalHeaders = "content-type:application/json; charset=utf-8\nhost:" . $host . "\n";
$signedHeaders = "content-type;host";
$canonicalRequest = $method . "\n/\n\n" . $canonicalHeaders . "\n" . $signedHeaders . "\n" . $payloadHash;
// 步骤2:拼接待签名字符串
$credentialScope = $date . "/" . $service . "/tc3_request";
$hashedCanonicalRequest = hash('sha256', $canonicalRequest);
$stringToSign = "TC3-HMAC-SHA256\n" . $timestamp . "\n" . $credentialScope . "\n" . $hashedCanonicalRequest;
// 步骤3:计算签名
$secretDate = hash_hmac('sha256', $date, "TC3" . $secretKey, true);
$secretService = hash_hmac('sha256', $service, $secretDate, true);
$secretSigning = hash_hmac('sha256', "tc3_request", $secretService, true);
$signature = hash_hmac('sha256', $stringToSign, $secretSigning);
// 步骤4:拼接Authorization
$authorization = "TC3-HMAC-SHA256 Credential=" . $secretId . "/" . $credentialScope . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature;
return $authorization;
}
/**
* 过滤参数
*
* @param array $params 原参数
* @param array $excludeKeys 需要排除的键
* @return array
*/
protected static function filterParams(array $params, array $excludeKeys = []): array
{
$excludeKeys = array_merge(['sign', 'signature', 'sign_type'], $excludeKeys);
$result = [];
foreach ($params as $key => $value) {
// 排除指定的键
if (in_array($key, $excludeKeys)) {
continue;
}
// 过滤空值(保留0和'0')
if ($value === '' || $value === null || $value === false) {
continue;
}
// 数组转为JSON
if (is_array($value)) {
$value = json_encode($value);
}
$result[$key] = $value;
}
return $result;
}
/**
* 构建签名字符串
*
* @param array $params 参数
* @param string $separator 分隔符
* @return string
*/
protected static function buildSignStr(array $params, string $separator = '&'): string
{
$str = '';
foreach ($params as $key => $value) {
if ($str !== '') {
$str .= $separator;
}
$str .= $key . '=' . $value;
}
return $str;
}
/**
* MD5 签名
*/
protected static function md5Sign(string $str, string $key): string
{
return md5($str . $key);
}
/**
* SHA1 签名
*/
protected static function sha1Sign(string $str, string $key): string
{
return sha1($str . $key);
}
/**
* SHA256 签名
*/
protected static function sha256Sign(string $str, string $key): string
{
return hash('sha256', $str . $key);
}
/**
* HMAC 签名
*/
protected static function hmacSign(string $str, string $key, string $algo): string
{
return hash_hmac($algo, $str, $key);
}
/**
* RSA 签名
*/
protected static function rsaSign(string $str, string $key, ?string $privateKey): string
{
if (!$privateKey) {
throw new InvalidArgumentException('RSA签名需要提供私钥');
}
$privateKey = openssl_pkey_get_private($privateKey);
if (!$privateKey) {
throw new RuntimeException('无效的RSA私钥');
}
$signature = '';
openssl_sign($str, $signature, $privateKey, OPENSSL_ALGO_SHA256);
openssl_free_key($privateKey);
return base64_encode($signature);
}
/**
* ECDSA 签名
*/
protected static function ecdsaSign(string $str, string $key, ?string $privateKey): string
{
if (!$privateKey) {
throw new InvalidArgumentException('ECDSA签名需要提供私钥');
}
$privateKey = openssl_pkey_get_private($privateKey);
if (!$privateKey) {
throw new RuntimeException('无效的ECDSA私钥');
}
$signature = '';
openssl_sign($str, $signature, $privateKey, OPENSSL_ALGO_SHA256);
openssl_free_key($privateKey);
return base64_encode($signature);
}
/**
* URL编码(RFC 3986)
*
* @param string $str
* @return string
*/
protected static function percentEncode(string $str): string
{
$res = urlencode($str);
$res = str_replace(['+', '*', '%7E'], ['%20', '%2A', '~'], $res);
return $res;
}
}
// ==================== 使用示例 ====================
class SignUtilExample
{
public function testBasicSign()
{
$params = [
'name' => '张三',
'age' => 30,
'phone' => '13800138000',
];
$secret = 'my_secret_key';
// MD5签名
$sign = SignUtil::sign($params, $secret, SignUtil::ALG_MD5);
echo "MD5签名: " . $sign . PHP_EOL;
// HMAC-SHA256签名
$sign = SignUtil::sign($params, $secret, SignUtil::ALG_HMAC_SHA256);
echo "HMAC-SHA256签名: " . $sign . PHP_EOL;
// 验证签名
$params['sign'] = $sign;
$result = SignUtil::verify($params, $secret, SignUtil::ALG_HMAC_SHA256);
echo "签名验证: " . var_export($result, true) . PHP_EOL;
}
public function testWechatSign()
{
$params = [
'appid' => 'wx123456789',
'mch_id' => '123456',
'nonce_str' => 'abc123',
'body' => '测试商品',
'out_trade_no' => '2024000001',
'total_fee' => 100,
];
$apiKey = 'your_wechat_pay_api_key';
$sign = SignUtil::wechatSign($params, $apiKey);
echo "微信支付签名: " . $sign . PHP_EOL;
}
public function testGenerateRSAKeyPair()
{
// 生成RSA密钥对
$config = [
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
];
$res = openssl_pkey_new($config);
openssl_pkey_export($res, $privateKey);
$details = openssl_pkey_get_details($res);
$publicKey = $details['key'];
$params = ['order_id' => '123456', 'amount' => 100];
// 使用私钥签名
$sign = SignUtil::sign($params, '', SignUtil::ALG_RSA, [
'private_key' => $privateKey
]);
// 使用公钥验证(需要额外实现)
echo "RSA私钥: " . $privateKey . PHP_EOL;
echo "RSA公钥: " . $publicKey . PHP_EOL;
echo "RSA签名: " . $sign . PHP_EOL;
}
}
// 执行示例
$example = new SignUtilExample();
$example->testBasicSign();
$example->testWechatSign();
$example->testGenerateRSAKeyPair();
这个签名工具类具有以下特点:
主要功能
- 多算法支持:MD5、SHA1、SHA256、HMAC系列和RSA/ECDSA
- 参数过滤:自动过滤空值和签名相关字段
- 密钥拼接:支持密钥直接拼接方式
- 防时序攻击:使用
hash_equals()验证签名 - URL编码:支持RFC 3986标准编码
使用场景
- API接口安全:防止参数篡改
- 支付系统:微信支付、支付宝等平台签名
- 开放平台:类似于阿里云、腾讯云的签名机制
- JWT令牌:JWT签名的基础
扩展建议
如果需要更多平台的特定签名,可以在类中添加:
// 支付宝签名
public static function alipaySign(array $params, string $privateKey) {}
// 百度云签名
public static function baiduSign(array $params, string $secret) {}
// JWT签名
public static function jwtSign(array $payload, string $secret) {}
这个工具类应该能满足大多数PHP项目的签名需求,如果有特定平台的需求,可以根据具体规范进行定制扩展。