189 lines
6.8 KiB
PHP
189 lines
6.8 KiB
PHP
<?php
|
|
namespace kuaiqian;
|
|
|
|
use Exception;
|
|
|
|
class CryptoProcessor
|
|
{
|
|
|
|
//商户证书
|
|
private $merchantCert;
|
|
|
|
//商户私钥
|
|
private $merchantKey;
|
|
|
|
//快钱证书
|
|
private $kuaiqianCert;
|
|
|
|
private $temp_path = PLUGIN_ROOT.'kuaiqian/temp/';
|
|
|
|
public function __construct($merchantCertPath, $merchantCertPath_password, $kuaiqianCertPath)
|
|
{
|
|
$pfx = file_get_contents($merchantCertPath);
|
|
if(!openssl_pkcs12_read($pfx, $certs, $merchantCertPath_password)){
|
|
throw new Exception("商户证书读取失败!");
|
|
}
|
|
$this->merchantCert = $certs['cert'];
|
|
$this->merchantKey = $certs['pkey'];
|
|
$this->kuaiqianCert = file_get_contents($kuaiqianCertPath);
|
|
}
|
|
|
|
|
|
/**
|
|
* 商户端加密加签
|
|
* @param String $originalData 加密前明文
|
|
* @param String $salt 盐值,防止并发请求下,加解密txt文件的内容被覆写,可自定义
|
|
* @return string 请求快钱的body
|
|
*/
|
|
public function seal(string $originalData,string $salt){
|
|
$Body_final['signedData'] = $this->getSignedData($originalData,$salt);
|
|
$Body_final['envelopedData'] = $this->getEnvelopedData($originalData,$salt);
|
|
if(0==strlen($Body_final['signedData']) || 0==strlen($Body_final['envelopedData'])){
|
|
throw new Exception("请求出错,signedData或envelopedData为空!");
|
|
}
|
|
return $Body_final;
|
|
}
|
|
|
|
/**
|
|
* 商户端解密验签
|
|
* @param String $signedData 快钱返回的签名
|
|
* @param String $envelopedData 快钱返回的密文
|
|
* @param String $salt 盐值,防止并发请求下,加解密txt文件的内容被覆写,可自定义
|
|
* @return string 解密后的明文
|
|
*/
|
|
public function unseal(string $signedData,string $envelopedData,string $salt){
|
|
$responseDecryptData = $this->getDecryptData($envelopedData,$salt);
|
|
$verifyResult = $this->getVerifyFlag($responseDecryptData,$signedData,$salt);
|
|
if(0==strlen($responseDecryptData)){
|
|
throw new Exception("客户端解密失败!");
|
|
}
|
|
if(!$verifyResult){
|
|
throw new Exception("客户端验签失败!");
|
|
}
|
|
return $responseDecryptData;
|
|
}
|
|
|
|
/**
|
|
* 获取密文 快钱证书加密
|
|
*/
|
|
public function getEnvelopedData(string $originalData,string $salt):string {
|
|
//定义一个data文件,写入明文body
|
|
$originalDataPath = $this->temp_path . 'data_' . $salt . '.txt';
|
|
if(!file_put_contents($originalDataPath, $originalData)){
|
|
throw new Exception("获取密文失败,写入文件失败!");
|
|
}
|
|
//获取证书内容
|
|
$publickey = $this->kuaiqianCert;
|
|
//定义一个endata文件,存放加密后数据
|
|
$enDataPath = $this->temp_path . 'endata_' . $salt . '.txt';
|
|
openssl_pkcs7_encrypt($originalDataPath,$enDataPath,$publickey,null,
|
|
PKCS7_BINARY,OPENSSL_CIPHER_AES_128_CBC);
|
|
//获取密文及字符处理
|
|
$enData = file_get_contents($enDataPath);
|
|
$finalEnData = str_replace(array("\r\n","\r","\n","\\"),"",
|
|
substr($enData,191,strlen($enData)));
|
|
//返回
|
|
unlink($originalDataPath);
|
|
unlink($enDataPath);
|
|
return $finalEnData;
|
|
}
|
|
|
|
/**获取签名 商户证书签名
|
|
* @return Base64string
|
|
*/
|
|
public function getSignedData(string $originalData,string $salt):string {
|
|
$originalDataPath = $this->temp_path . 'origdata_' . $salt . '.txt';
|
|
if(!file_put_contents($originalDataPath, $originalData)){
|
|
throw new Exception("获取签名失败,写入文件失败!");
|
|
}
|
|
$signdataPath = $this->temp_path . 'signdata_' . $salt . '.txt';
|
|
openssl_pkcs7_sign($originalDataPath,$signdataPath,
|
|
$this->merchantCert,
|
|
$this->merchantKey,
|
|
[],
|
|
PKCS7_BINARY);
|
|
$signdata = file_get_contents($signdataPath);
|
|
$finalsigndata = str_replace(array("\r\n","\r","\n"),array(""),
|
|
substr($signdata,186,strlen($signdata)));
|
|
unlink($originalDataPath);
|
|
unlink($signdataPath);
|
|
return $finalsigndata;
|
|
}
|
|
|
|
/**返回解密 商户证书解密
|
|
* @return string
|
|
*/
|
|
public function getDecryptData(string $encryptoData,string $salt):string {
|
|
$respdecryptoDataPath = $this->temp_path . 'respDecryptoData_' . $salt . '.txt';
|
|
//txt内容须遵守SMIME格式规范,请勿做增删、对齐等操作
|
|
$txt ="MIME-Version: 1.0
|
|
Content-Disposition: attachment; filename=\"smime.p7m\"
|
|
Content-Type: application/x-pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\"
|
|
Content-Transfer-Encoding: base64"."\n\n\n".$encryptoData;
|
|
if(!file_put_contents($respdecryptoDataPath, $txt)){
|
|
throw new Exception("返回解密失败,写入文件失败!");
|
|
}
|
|
$decryptoDataPath = $this->temp_path . 'decryptoData_' . $salt . '.txt';
|
|
if(openssl_pkcs7_decrypt($respdecryptoDataPath,$decryptoDataPath,
|
|
$this->merchantCert,
|
|
$this->merchantKey)){
|
|
$decryptoData = file_get_contents($decryptoDataPath);
|
|
//Log::info('解密成功!快钱返回明文body为:'.$decryptoData);
|
|
unlink($decryptoDataPath);
|
|
unlink($respdecryptoDataPath);
|
|
return $decryptoData;
|
|
}else{
|
|
unlink($respdecryptoDataPath);
|
|
unlink($decryptoDataPath);
|
|
throw new Exception('返回数据解密失败!failed to decrypt!');
|
|
}
|
|
}
|
|
|
|
/**返回验签 快钱证书验签
|
|
* @return bool
|
|
*/
|
|
public function getVerifyFlag(string $decryptoData,string $signedData,string $salt):bool {
|
|
$respsignedDataPath = $this->temp_path . 'respSignedData_' . $salt . '.txt';
|
|
$txt =$signedData;
|
|
file_put_contents($respsignedDataPath,$this->formatSmimeSignData($txt,$decryptoData));
|
|
|
|
$unSignDataPath = $this->temp_path . 'unSignData_' . $salt . '.txt';
|
|
|
|
$flag = openssl_pkcs7_verify($respsignedDataPath,PKCS7_NOVERIFY,$unSignDataPath);
|
|
|
|
unlink($respsignedDataPath);
|
|
unlink($unSignDataPath);
|
|
|
|
return $flag == 1;
|
|
}
|
|
|
|
/**
|
|
* @return String $signData 内容须遵守SMIME格式规范,请勿做增删、对齐等操作
|
|
*/
|
|
public function formatSmimeSignData($txt,$decryptoData)
|
|
{
|
|
$signData = chunk_split($txt, 76, "\n");
|
|
$boundary = "----" . md5($signData);
|
|
$signData = <<<EOD
|
|
MIME-Version: 1.0
|
|
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg=sha256; boundary="$boundary"
|
|
|
|
This is an S/MIME signed message
|
|
|
|
--$boundary
|
|
$decryptoData
|
|
--$boundary
|
|
Content-Type: application/x-pkcs7-signature; name="smime.p7s"
|
|
Content-Transfer-Encoding: base64
|
|
Content-Disposition: attachment; filename="smime.p7s"
|
|
|
|
$signData
|
|
|
|
--$boundary--
|
|
|
|
|
|
EOD;
|
|
|
|
return $signData;
|
|
}
|
|
} |