1.配对的公钥,私钥:
//公钥(注意包括头(-----BEGIN PUBLIC KEY-----)尾(-----END PUBLIC KEY-----)标志)
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbdq5NjtUEkAQ6wucPuhC0aRvSMsaX3GrhkwsLLdWZnVNVpkJRw
yPFq9HJNuntRw7P9Sb3TkwrpN60x62kZ6qV8h1GoG4jIfofuVWPv1VzudAV6kWJWMl3sc
+DtV5q1dy1KQLb6e90cuOynzxVT3j+Fx7ZOzovmdTkEcoRiYWV1QIDAQAB
-----END PUBLIC KEY-----
//私钥(注意包括头(-----BEGIN RSA PRIVATE KEY-----)尾(-----END RSA PRIVATE KEY-----)标志)
-----BEGIN RSA PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKBc0G8WmfydUuX6TnO5M3lSmNYqy8beEgBn24vWk6sWCZ2va+TRIWEew5dxmMXg5+N40a2XOJfyTxCALvzts+/J0q8efi2WIecPecWGNoDB8ZehGfzv3BQD5skk9sf1tL6ztU5tquPFajwmO85WR2pTFfk0CXgPEtLzPxntYfHpAgMBAAECgYB6DSo24qTgt3zYvOHpAhRDSrI4jun5uhtJY8Kdc/uAQ42eDl24EdOt75Q+N59nO/5CCzrLPEU+oJW8oVMh/mjr6gMv7QxhEr1iUrZmvAGTO2kB5sMgWfqPkiHeGddYWawcWGhAwQEJrrgGVldzaslY9FvWcmTjCjypEVvB7l1BgQJBAOJwB2RbRbI+z0d1M6DOawyu+J7JleMAQ42x9KyhiwYc/D0CZX0gkuyqGcwSh+c1CYjjdoRIh/531e1OZyBmRG0CQQC1TG68k+iXuz14+fgpHEttl2E3lQ6VfIzv+oaQHnRmmT92t2Eci70zkDFjVCGXxwQ279EokmP6RWfJG5Z1nV3tAkAZkHpVKzTQLeUq9SFyCzvsv6hUDQA+E56M1cWA4/AVLZqQrL+Wg+HylDW7Y3AyeztrV/rebm3kHdVqKEreTo11AkBPUou40nYXvQKeZbAgPJL79hnA+eSRnxcDAHfTop+HLFHKHV3N4Y38e4BAV1UDVT4Q00iOGc7Id4l7QijIePvZAkEA1OmJfbsaCx3T+4lZahzo8k7NzP5BS6izPVAVk8O3LHo3TggJZMa69Nc+8O82ZXOiOSt7bSONaST54ejjN8yhtg==
-----END RSA PRIVATE KEY-----
2.判断openssl扩展是否安装
extension_loaded('openssl') or die('PHP加密需要openssl扩展支持');
3.判断公钥和私钥是否可用
//私钥文件路径
$this->privateKey = file_get_contents(dirname(__FILE__) .'libraries/YCPAY/account/rsa_private_key.pem');
//公钥文件路径
$this->publicKey = file_get_contents(dirname(__FILE__) . 'libraries/YCPAY/account/rsa_public_key.pem');
/**
* 生成Resource类型的密钥,如果密钥文件内容被破坏,openssl_pkey_get_private函数返回false
*/
$privateKey = openssl_pkey_get_private($this->privateKey);
/**
* 生成Resource类型的公钥,如果公钥文件内容被破坏,openssl_pkey_get_public函数返回false
*/
$publicKey = openssl_pkey_get_public($this->publicKey);
($privateKey) or die('密钥不可用');
($publicKey) or die('公钥不可用');
4.公钥加密
由于openssl_public_encrypt()加密对加密串有字符限制(117字节),所有需要分段加密(每段32个字符,加密后拼接起来)
define("BAOFOO_ENCRYPT_LEN", 32);
// 公钥加密
function encryptedByPublicKey($data_content)
{
$publicKey = openssl_pkey_get_public($this->publicKey);
$encrypted = "";
$totalLen = strlen($data_content);
$encryptPos = 0;
while ($encryptPos < $totalLen) {
// openssl_public_encrypt(substr($data_content, $encryptPos, BAOFOO_ENCRYPT_LEN), $encryptData, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
openssl_public_encrypt(substr($data_content, $encryptPos, BAOFOO_ENCRYPT_LEN), $encryptData, $publicKey);
// $encrypted .= bin2hex($encryptData);
$encrypted .= base64_encode($encryptData);
$encryptPos += BAOFOO_ENCRYPT_LEN;
}
return $encrypted;
}
5.私钥解密
由于openssl_private_decrypt()加密对加密串有字符限制(128字节),所有需要分段解密后拼接起来
// 私钥解密
function encryptedByPrivateKey($data_content)
{
$privateKey = openssl_pkey_get_private($this->privateKey);
$crypto = '';
$data_content = base64_decode($data_content);
foreach (str_split($data_content, 128) as $chunk) {
openssl_private_decrypt($chunk, $decryptData, $privateKey);
$crypto .= $decryptData;
}
return $crypto;
}
6.完整代码如下,仅供参考
define("BAOFOO_ENCRYPT_LEN", 32);
//CI框架
class YcPay extends MY_Controller
{
private $privateKey;
private $publicKey;
private $merIdNum;
private $merKey;
private $url;
public function __construct()
{
parent::__construct();
//正式环境
$this->privateKey = file_get_contents(APPPATH . 'libraries/YCPAY/account/rsa_private_key.pem');
$this->publicKey = file_get_contents(APPPATH . 'libraries/YCPAY/account/rsa_public_key.pem');
$this->merIdNum = 'dc0020663';
$this->merKey = '5446042869aaf86d22c5d19189f21956';
$this->url = 'https://mis.ixianhe.cn/action/exp/';
}
// 公钥加密
function encryptedByPublicKey($data_content)
{
// $data_content = base64_encode($data_content);
$publicKey = openssl_pkey_get_public($this->publicKey);
$encrypted = "";
$totalLen = strlen($data_content);
$encryptPos = 0;
while ($encryptPos < $totalLen) {
// openssl_public_encrypt(substr($data_content, $encryptPos, BAOFOO_ENCRYPT_LEN), $encryptData, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
openssl_public_encrypt(substr($data_content, $encryptPos, BAOFOO_ENCRYPT_LEN), $encryptData, $publicKey);
// $encrypted .= bin2hex($encryptData);
$encrypted .= base64_encode($encryptData);
$encryptPos += BAOFOO_ENCRYPT_LEN;
}
return $encrypted;
}
// 私钥解密
function encryptedByPrivateKey($data_content)
{
$privateKey = openssl_pkey_get_private($this->privateKey);
$crypto = '';
$data_content = base64_decode($data_content);
foreach (str_split($data_content, 128) as $chunk) {
openssl_private_decrypt($chunk, $decryptData, $privateKey);
$crypto .= $decryptData;
}
return $crypto;
}
//检测密钥+加密
public function rsa_pass($params, $api_method, $is_big)
{
extension_loaded('openssl') or die('php需要openssl扩展支持');
/**
* 生成Resource类型的密钥,如果密钥文件内容被破坏,openssl_pkey_get_private函数返回false
*/
$privateKey = openssl_pkey_get_private($this->privateKey);
/**
* 生成Resource类型的公钥,如果公钥文件内容被破坏,openssl_pkey_get_public函数返回false
*/
$publicKey = openssl_pkey_get_public($this->publicKey);
// ($privateKey) or die('密钥不可用');
// ($publicKey) or die('公钥不可用');
if (empty($privateKey)) {
$param_back = [
'resCode' => '9999',
'resMsg' => '密钥不可用',
];
$this->log($param_back, 'rsa_pass', $is_big, '错误报文:');
echo json_encode($param_back, JSON_UNESCAPED_UNICODE);
exit;
}
if (empty($publicKey)) {
$param_back = [
'resCode' => '9999',
'resMsg' => '公钥不可用',
];
$this->log($param_back, $api_method, $is_big, '错误报文:');
echo json_encode($param_back, JSON_UNESCAPED_UNICODE);
exit;
}
/**
* 原数据
*/
$originalData = json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// var_dump(strlen($originalData));die;
//用公钥加密
$encryptData = $this->encryptedByPublicKey($originalData);
return $encryptData;
}
/**
* 签名 + post请求
* @param array $params 请求参数
* @param string $method 第三方方法名
* @param string $api_method 接口名
* @param string $is_big 通道别名
* @param string $bankcardNo 银行卡号
* @return mixed
*/
public function sign_post($params, $method, $api_method, $is_big, $bankcardNo = '')
{
$this->log($params, $api_method, $is_big, '业务数据:', $bankcardNo);
//公钥加密(RSA/ECB/PKCS1Padding)
$encryptData = $this->rsa_pass($params, $api_method, $is_big);
$merSign = "merIdNum=" . $this->merIdNum . "&encryptData=" . $encryptData . "&merKey=" . $this->merKey;
//公共参数
$request_param = [
'merVersion' => '3.0',
'merIdNum' => $this->merIdNum,
'requestMethod' => 'exp.' . $method,
'merSign' => md5($merSign),
'encryptData' => $encryptData,
];
$requestUrl = $this->url . $method;
$this->log($requestUrl, $api_method, $is_big, '请求url:');
$this->log($request_param, $api_method, $is_big, '请求报文:');
$request_param = json_encode($request_param, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$res = $this->curl($requestUrl, $request_param);
$this->log($res, $api_method, $is_big, '响应报文:');
// $third_array = [
// 'userStatus'=>'E0000',
// 'userMsg'=>'绑卡申请成功',
// 'encryptData'=>'lVa1eNpFP5V6ImaFbk2s2mu7ptY8cHc+ANrI3o38ZTdeR+OfZsOA1BeWD8vDacBl7eFA7GgTA6OM2mzIuwlT4wkgm8IpNshRwtKHwTAri0c4HsZ+2qTfZJjybiB36eJ3CsiPntlae7wsMcNx9hxlDvcs19D77mufEVhTWYQYcHc=',
// 'merIdNum'=>'dc0020450',
// 'merSign'=>'79d52ca53003c3c7b73916dac313c38a',
// ];
// $this->log($third_array, $api_method, $is_big, '响应报文:');
$third_array = json_decode($res, true);
if ($third_array['userStatus'] == 'E0000' && isset($third_array['encryptData'])) {
//私钥解密
$third_res = $this->encryptedByPrivateKey($third_array['encryptData']);
$this->log($third_res, $api_method, $is_big, '解密报文:');
//处理返回
$third_array['data'] = json_decode($third_res);
//解密报文:{"orderDesc":"交易成功","orderStatus":"SUCCESS","userLinkId":"YCDA9CA_257919_162701706280078"}
} else {
//失败响应的两种情况
//【2021-07-22 15:56:23】响应报文:{"userStatus":"E0000","userMsg":"结算请求已受理","encryptData":"Rfyvdj3HUOtGzMAbP4Jh4EQpd9VXDr7BLQD5fRwz+TOvCwI6EubNv4MdXCWuRN1zm9C17bvt27jx1I67ceVHhFW/vpqNEBG5eTmqJjXP6PJrJwiyf03yrK3EUOXf7wgpdAH5NW3PrywrWLmt/lC2hNvUf02iQ0gXRxN3Ka6X9k4a1QfjSJRXYTXdUlVshDWx+Y1gMKRvLTrOW4IzaJpyYpd/KXI7ahEAYqWCqEruPhRIUV9N+z+lECHIeG0Ml/sC7/AdBTs4oT+LgMNGsxCH5n1RoFj4EhqcVCrrrYW5O900sdrn78xURWo3yvkL2up6vDMV9fmViV2udwAVC+wD6Q==","merIdNum":"dc0020663","merSign":"342e58237f3e68213f23815410585a9b"}
//【2021-07-22 15:56:23】解密报文:{"orderDesc":"商户手续费金额超过了最大手续费限制","orderStatus":"FAIL","userLinkId":"YCDA9CA_162990_162694058243001"}
//------------------------
//【2021-07-23 11:01:03】响应报文:{"userStatus":"E0002","userMsg":"用户余额不足"}
//这样处理了之后前端才不会出错
$third_array['data'] = [
"orderDesc" => $third_array['userMsg'],
"orderStatus" => "FAIL",//订单状态 SUCCESS 交易成功 FAIL交易失败 PROCESSING 交易处理中
];
}
$third_json = json_encode($third_array, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return $third_json;
}
//接口post请求
public function curl($url = '', $data = '')
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
if (!empty($data)) {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30); //超时时间30秒
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$headers = [
'Content-type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
/**
* 日志记录
* @param $data /内容
* @param $method /方法名
* @param $bankcardNo /银行卡号
* @param string $is_big /通道别名
* @param string $title /标题
*/
public function Log($data, $method, $is_big, $title, $bankcardNo = '')
{
$str = is_array($data) ? json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $data;
if (empty($bankcardNo)) {
$content = '【' . date('Y-m-d H:i:s') . '】' . $title . $str . PHP_EOL;
} else {
$content = ' bankcardNo:' . $bankcardNo . '【' . date('Y-m-d H:i:s') . '】' . $title . $str . PHP_EOL;
}
$path = APPPATH . "../PAYLOG/$is_big/$method/";
if (!is_dir($path)) { //判断目录是否存在 不存在就创建
mkdir($path, 0777, true);
}
file_put_contents($path . date("Y-m-d") . '.txt', $content, FILE_APPEND);
//换行分割
file_put_contents($path . date("Y-m-d") . '.txt', '------------------------' . PHP_EOL, FILE_APPEND);
}
}
openssl_private_encrypt($data,$encrypted,$this->privateKey);//私钥加密
openssl_public_decrypt($encrypted,$decrypted,$this->publicKey);//私钥加密的内容通过公钥可用解密出来
参考博文:openssl_sign() 语法+RSA公私钥加密解密,非对称加密算法详解-CSDN博客