可变性
时效性
唯一性
完整性
步骤
RSA公钥密钥生成
OpenSSL> genrsa -out rsa_private_key.pem 2048 #生成私钥
OpenSSL> pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out rsa_private_key_pkcs8.pem #Java开发者需要将私钥转换成PKCS8格式
OpenSSL> rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem #生成公钥
OpenSSL> exit #退出OpenSSL程序
#rsa_public_key.pem 和 rsa_private_key.pem 即为所需
下面给出一套RSA和md5的整合签名代码给予参考。
<?php
class Sign
{
protected $md5Key = 'c4ca4238a0b923820dcc509a6f75849b';//公钥
protected $md5secret = '28c8edde3d61a0411511d3b1866f0636';//私钥
protected $md5invalid = 600;
protected $publicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiSvrdwvEjgeh+/sgY+QPowx+rE/Ou17yM4iFAnQEugi9MqrRX+x+Y0PTBoenqmH+qis79LaS4R3BL6Fi2F83EQBIDk38RDrzDYrlpTCbKg4iPCPCDOolxSNlF4xs9z2feb2QQmYcJ7H/QISabCnEV/U9TJK+bUlMGySQ5vpwImKioTvQ/R4vMwNi0R3NZH6IwTPukgs0wIlIEQ/SHH7ZurMobfaXHkqQLgFdH38MDsSfoLYunWtfCnxsbQa/z5qPDcWbvAY569UjcMPUz3Me4oOo2VMpOgVYFJRe3KhBpC4vx5NSHxBAHXrb6uO5j5kmEFIRMpW4PXONQnmrpN93ZwIDAQAB';
protected $privateKey = 'MIIEogIBAAKCAQEAiSvrdwvEjgeh+/sgY+QPowx+rE/Ou17yM4iFAnQEugi9MqrRX+x+Y0PTBoenqmH+qis79LaS4R3BL6Fi2F83EQBIDk38RDrzDYrlpTCbKg4iPCPCDOolxSNlF4xs9z2feb2QQmYcJ7H/QISabCnEV/U9TJK+bUlMGySQ5vpwImKioTvQ/R4vMwNi0R3NZH6IwTPukgs0wIlIEQ/SHH7ZurMobfaXHkqQLgFdH38MDsSfoLYunWtfCnxsbQa/z5qPDcWbvAY569UjcMPUz3Me4oOo2VMpOgVYFJRe3KhBpC4vx5NSHxBAHXrb6uO5j5kmEFIRMpW4PXONQnmrpN93ZwIDAQABAoIBAC0a4gx9NB63583h39646WNmAmlKvOHj8KR9aa9K0xsRMJVukfaG33BopwVoqfteyczO9qIbPuUDUbkFymj3tjXC7+60OhV9hNqZJ7ZP61XC3AMGhxKUE+NlJiK+LD6IZt4zNTKAPRXYc+SVNeoHOebqX0PEpRVumrX6KiOpiiHj6l8AMTih6/KjMu53cqhPCbG+FHrlAUy9GWD/+J2DsIPUAZGhFfNt/coXOO32XgHwS90lrn2a0K4c72pwM37tjMjeddOWR9HjtY/PW4oRHJhTsmAWUzHH1oOt06sHBgfVB6X7SaFzOITgIU7sClbwwNUCzF3DaqO6MuqldX4fiFECgYEA7mBn7KlXJ6EuB5XjUib2kLZD6SV0Oe9/Bd3wWMMJHfCPBNSDdUOVYrEndc9rq4yvEpVnvJ5loMASAVbUwphp7mOpv3sl1asJmJFRGndtspHs3TgkfmyThMMTGl7UHSKyWo/SpJXlPlncfnavUh07emwIZF4hPUqQAkP6/0E8zo0CgYEAk1AS9/dFcVY1j3sQRclCeYS0H3O/S8P30gigWIVi2FRc9AnwfPZ7eiHd+lprSNdBaW+9ZxlzSpMlSTztL0kXcmCn6N5lO8o+qO7ti1sgNw3xQp2iSRsBKH/CU52cDztJtUw+yy2LWUpAX5p82mdIB2ymOP/jc8697N6zgtcXKsMCgYAE/YevcKwebEVma0DjC2XGCcrKKrqQK+9g1BCgCxU5xzt3QmuuHMgX1NWapcj/Qma34ODXFgnSn7LAzGyP1lkBYJzBIXbdTkNZKlGkWDO3tU5cIzzAWM2NzfesaafPJFbPhotGXsz5zS/MhfeNpIcGPRS/5SiU++af5YRvq5H2UQKBgHXbnaF32r4ne+iUS9uZfq6cRkPXphfm7JHExwyrgv6S2F+CyD4iMX3wNJmE18rKNRI3DPC8guoKOc2TiivHrZOb0xrTO2kPkPw1VCWnPWnupLRoS5tzmISfWojtUxs4kusS2jZR9Of2KPSUNAnEkfMmsQJvb7mKkZc+QZ6PmYBjAoGAN0EGeGV1nMfHnzqsjUt0bEK5SNAztoLeLt8Z1FvFk5nPSdOIsNCr9X7BtvPtvHEmdocevOZ+aJO4rIH2N4SkkHLzXevZnDzmoq5NVRNdUE5/zHHRf56NwNbqZdDz3Wfkwyx+hMBZROKt+K+aqi+Vbj/hpKjYbqycmMgdcV5rxo4=';
protected $tag;
const MD5 = 'md5';
const RSA = 'rsa';
const RSA2 = 'rsa2';
public function __construct($tag = 'md5')
{
$this->tag = strtolower($tag);
}
/**
* @param array $data
* @return array|mixed|string
*/
public function makeSign($data = [])
{
if (empty($data)) static::message(['code' => 202, 'msg' => '签名数据为空!']);
$data['timestamp'] = time();
$data['ip'] = static::getClientIp();
switch ($this->tag) {
case self::MD5:
$data['key'] = $this->md5Key;
$this->message(['code' => 200, 'msg' => '签名成功!', 'data' => $this->makeMd5Sign($data)]);
break;
case self::RSA:
case self::RSA2:
$this->message(['code' => 200, 'msg' => '签名成功!', 'data' => $this->makeRsaSign($data)]);
break;
default:
$this->message(['code' => 202, 'msg' => 'tag错误!']);
}
}
/**
* @param array $data
*/
public function verifySign($data = [])
{
if (empty($data)) static::message(['code' => 203, 'msg' => '验签数据为空!']);
if (!isset($data['sign']) || !$data['sign']) static::message(['code' => 204, 'msg' => '数据签名不存在!']);
if (!isset($data['timestamp']) || !$data['timestamp']) static::message(['code' => 205, 'msg' => '发送的数据参数不合法!']);
if (time() - $data['timestamp'] > $this->md5invalid) static::message(['code' => 207, 'msg' => '验证失效, 请重新发送请求!']);
switch ($this->tag) {
case self::MD5:
$this->verifyMd5Sign($data);
break;
case self::RSA:
case self::RSA2:
$this->verifyRsaSign($data);
break;
default:
$this->message(['code' => 202, 'msg' => 'tag错误!']);
}
}
public function makeRsaSign($data)
{
if (isset($data['sign'])) unset($data['sign']);
ksort($data);
$params = urldecode(http_build_query($data));
$search = [
"-----BEGIN RSA PRIVATE KEY-----",
"-----END RSA PRIVATE KEY-----",
"\n",
"\r",
"\r\n"
];
$privateKey = str_replace($search, "", $this->privateKey);
$privateKey = $search[0] . PHP_EOL . wordwrap($privateKey, 64, "\n", true) . PHP_EOL . $search[1];
$res = openssl_get_privatekey($privateKey);
if ($res) {
if (self::RSA == $this->tag) {
openssl_sign($params, $sign, $res);
} else {
openssl_sign($params, $sign, $res, OPENSSL_ALGO_SHA256);
}
openssl_free_key($res);
} else {
static::message(['code' => 300, 'msg' => '私钥格式有误!']);
exit;
}
$data['sign'] = base64_encode($sign);
return $data;
}
public function verifyRsaSign($data)
{
if (!isset($data['sign'])) $this->message(['code' => 301, 'msg' => 'sign不存在!']);
$sign = $data['sign'];
unset($data['sign']);
ksort($data);
$params = urldecode(http_build_query($data));
$search = [
"-----BEGIN PUBLIC KEY-----",
"-----END PUBLIC KEY-----",
"\n",
"\r",
"\r\n"
];
$publicKey = str_replace($search, "", $this->publicKey);
$publicKey = $search[0] . PHP_EOL . wordwrap($publicKey, 64, "\n", true) . PHP_EOL . $search[1];
$res = openssl_get_publickey($publicKey);
if ($res) {
if (self::RSA == $this->tag) {
$result = (bool)openssl_verify($params, base64_decode($sign), $res);
} else {
$result = (bool)openssl_verify($params, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
}
openssl_free_key($res);
} else {
static::message(['code' => 300, 'msg' => '公钥格式有误!']);
exit;
}
if ($result) {
static::message(['code' => 200, 'msg' => '验签成功!']);
}
static::message(['code' => 300, 'msg' => '验签失败!']);
}
/**
* @param array $data
* @return array|mixed
*/
protected function makeMd5Sign($data = [])
{
ksort($data);
$params = http_build_query($data);
$data['sign'] = md5($params . $this->md5secret);
unset($data['key']);
unset($data['ip']);
return $data;
}
/**
* @param array $data
*/
protected function verifyMd5Sign($data = [])
{
$sign = $data['sign'];
unset($data['sign']);
$data['ip'] = $this->getClientIp();
$data['key'] = $this->md5Key;
ksort($data);
$params = http_build_query($data);
if ($sign !== md5($params . $this->md5secret)) static::message(['code' => 208, 'msg' => '验签错误!']);
$this->message(['code' => 200, 'msg' => '验签通过!']);
}
/**
* @param array $msg
*/
static public function message($msg = [])
{
echo json_encode($msg);
exit();
}
/**
* @param int $type
* @return mixed
*/
protected function getClientIp($type = 0)
{
$type = $type ? 1 : 0;
static $ip = NULL;
if ($ip !== NULL) return $ip[$type];
if ($_SERVER['HTTP_X_REAL_IP']) {
$ip = $_SERVER['HTTP_X_REAL_IP'];
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown', $arr);
if (false !== $pos) unset($arr[$pos]);
$ip = trim($arr[0]);
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
$long = sprintf("%u", ip2long($ip));
$ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
return $ip[$type];
}
}
//MD5签名
//以用户提交抢购商品为例
$data = [
'username' => '17521181231',
'sex' => 1,
'age' => 18,
'address' => '上海徐汇区xx'
];
$data = (new Sign())->makeSign($data);
//MD5验签
$data = [
'address' => '上海徐汇区xx',
'age' => 18,
'sex' => 1,
'timestamp' => 1640770444,
'username' => '17521181231',
'sign' => '28f62f3803d684b000a7efa9ef2a1f7c'
];
$result = (new Sign())->verifySign($data);
//RSA或者RSA2
$data = [
'username' => '17521181231',
'sex' => 1,
'age' => 18,
'address' => '上海徐汇区xx'
];
//RSA签名
$data = (new Sign('rsa'))->makeSign($data);
$data = [
'address' => '上海徐汇区xx',
'age' => 18,
'sex' => 1,
'timestamp' => 1640770444,
'username' => '17521181231',
'sign' => 'IucOoBdmubOtkWGKvz3OshVINXi0EjX6LsTPxddzvy4iu/RMTQVkvR22b3IY0VyUcJjOPjM5ZO7pwMv7XZEDahCcuHq2oCwVeGchVYB9MzGC2swvaFjpaJS5qa0LMb8wpwgo2wqqx7wO1nG+94Oxwp7S5+ko97YwF3+C7298raldLDFyUj8fKD1nEbhdRUfTcFmOH5JwiETLkd+uLkNexM/39y9N4z3YfqUfTwEivvybbVL8EIrkxOjdjZ9sc7lAUaUoiGvjScRsTF0GduDPDr1dYV6KagE6/CRYSuI6WMFJfmtRO/GvIaGjc/ha9+CIg60/Xshh0ntF2E3O1HOd5g=='
];
$result = (new Sign('rsa'))->verifySign($data);
//RSA2签名
$data = (new Sign('rsa2'))->makeSign($data);
$data = [
'address' => '上海徐汇区xx',
'age' => 18,
'sex' => 1,
'timestamp' => 1640770444,
'username' => '17521181231',
'sign' => 'IucOoBdmubOtkWGKvz3OshVINXi0EjX6LsTPxddzvy4iu/RMTQVkvR22b3IY0VyUcJjOPjM5ZO7pwMv7XZEDahCcuHq2oCwVeGchVYB9MzGC2swvaFjpaJS5qa0LMb8wpwgo2wqqx7wO1nG+94Oxwp7S5+ko97YwF3+C7298raldLDFyUj8fKD1nEbhdRUfTcFmOH5JwiETLkd+uLkNexM/39y9N4z3YfqUfTwEivvybbVL8EIrkxOjdjZ9sc7lAUaUoiGvjScRsTF0GduDPDr1dYV6KagE6/CRYSuI6WMFJfmtRO/GvIaGjc/ha9+CIg60/Xshh0ntF2E3O1HOd5g=='
];
//RSA2验签
$result = (new Sign('rsa2'))->verifySign($data);