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;
|
||
}
|
||
} |