工作中用到了非对称加密:RSA加解密及签名验证,根据查到的信息及工作中的问题总结,现在整理如下:
1. 准备好公钥和私钥,使用openssl工具生成RSA公钥和私钥对
1)生成RSA私钥:
genrsa -out rsa_private_key.pem 1024
该命令会生成1024位的私钥,可以在当前路径下看到rsa_private_key.pem文件。
2)把RSA私钥转换成PKCS8格式
输入命令pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM –nocrypt,并回车
得到生成功的结果,这个结果就是PKCS8格式的私钥,这个将密钥转换为PKCS8格式格式的只用在java语言中,PHP中不需要使用
3) 生成RSA公钥
输入命令rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem,并回车,
得到生成成功的结果,此时,我们可以看到一个文件名为rsa_public_key.pem的文件,打开它,可以看到-----BEGIN PUBLIC KEY-----开头,-----END PUBLIC KEY-----结尾的字符串,这个就是公钥。
2. RSA加密解密,项目中加解密使用的公私钥可能不同,但思路都是一样,以下贴出我加解密的方法及需要注意的问题:
_shangmei_privateKey, 'private'); // 私钥,调用key()函数拼接正确格式私钥字串,并验证私钥可用
$pri_key = file_get_contents('./Public/RSA/rsa_private_key.pem'); // 私钥,直接读取私钥文件,获取私钥
$pi_key = openssl_pkey_get_private($pri_key); // 获取私钥,这个函数可用来判断公钥是否是可用的,必须要有
$encrypted = '';
$data = json_encode($data);
//注意:最大允许加密长度为117,加密数据过长得分段加密
$plainData = str_split($data, 100);//生成密钥位数 1024 bit key
foreach($plainData as $chunk){
$partialEncrypted = '';
$encryptionOk = openssl_private_encrypt($chunk,$partialEncrypted,$pi_key);//私钥加密
if($encryptionOk === false){
$this->_message['response_status'] = -8;
$this->_message['data'] = '数据加密不成功';
sendJson($this->_message);
exit();
}else{
$encrypted .= $partialEncrypted;
}
}
$encrypted = base64_encode($encrypted);//加密后的内容通常含有特殊字符,需要编码转换下,在网络间通过url传输时要注意base64编码是否是url安全的
return $encrypted;
}
/**
* RSA公钥解密,使用项目中规定的公钥进行解密
* @return $decrypted json 返回解密后的json数据
*/
public function rsaPublicDecrypt(){
$pu_key = key($this->_zudian_publicKey, 'public'); // 调用key()方法
$decrypted = '';
$plainData = str_split(base64_decode(urldecode($this->_data)), 128);//生成密钥位数 1024 bit key
// 分段解密
foreach($plainData as $chunk){
$str = '';
$decryptionOk = openssl_public_decrypt($chunk,$str,$pu_key);//公钥解密
if($decryptionOk === false){
$this->_message['response_status'] = "-11";
$this->_message['data']="data解密不成功";
sendJson($this->_message);
exit();
}
$decrypted .= $str;
}
return $decrypted;
}
/**
* 公私钥字符串处理,拼接为正确的公私钥格式
* @param $key String 密钥字符串
* @param $type String 密钥类型,public:公钥 private:私钥
* @return $key String 返回获取的密钥
*/
public function key($key, $type){
//读取公钥文件
// $pubKey = file_get_contents('./rsa_public_key.pem');
//64个英文字符后接换行符"\n",最后再接换行符"\n"
$key = (wordwrap($key, 64, "\n", true))."\n";
//添加pem格式头和尾
if($type == 'public'){
$pubKey = "-----BEGIN PUBLIC KEY-----\n" . $key . "-----END PUBLIC KEY-----\n";
$key = openssl_pkey_get_public($pubKey); // 获取公钥,这个函数可用来判断公钥是否是可用的
}elseif ($type == 'private'){
$priKey = "-----BEGIN RSA PRIVATE KEY-----\n" . $key . "-----END RSA PRIVATE KEY-----\n";
$key = openssl_pkey_get_private($priKey); // 获取私钥,这个函数可用来判断公钥是否是可用的
}
return $key;
}
?>
3. RSA签名验证
$v){
$unsign .= $k.'='.$v.'&';
}
$unsign = substr($unsign, 0, strlen($unsign)-1); // 待签名字符串,去掉最后一个&字符
//如果存在转义字符,那么去掉转义
if(get_magic_quotes_gpc()){$unsign = stripslashes($unsign);}
unset($data);
unset($argus);
return $unsign;
}
/**
* 生成RSA签名,这个暂时没有用到,就先放在这
* @param array $data 待签名的数据
* return 签名结果
*/
public function rsaSign($unsign){
$priKey = file_get_contents('/rsa/rsa_private_key.pem'); // 本地私钥
$res = openssl_get_privatekey($priKey);
openssl_sign($unsign, $sign, $res);
openssl_free_key($res);
//base64编码
$sign = base64_encode($sign);
//对签名进行urlencode转码
$sign = urlencode($sign);
//生成最终签名信息
$orderInfor = $unsign."&sign=".$sign."&sign_type=RSA";
return $orderInfor;
}
/**
* 验证RSA签名信息,用户的私钥与对接方的公钥做交换
* @param string $unsign 用户传过来的待签名字符串,即根据rsaUnsign()生成的待签名数据
* @param string $rawSign 对接方传过来的待签名字符串,注意一般都会经过base64_decode编码,get传递还要经过urldecode编码,否则特殊字符在传递过程中容易被浏览器解析
* return 验签结果
*/
public function rsaVerify($unsign, $rawSign){
header('content-type:text/html;charset=utf-8');
$sign = urldecode($rawSign);
// $sign = base64_decode(str_replace(' ', '+', $sign));
$sign = base64_decode($sign);
//读取公钥文件
// $pubKey = file_get_contents('./rsa_public_key.pem');
//64个英文字符后接换行符"\n",最后再接换行符"\n"
$key = (wordwrap($this->_secretkey, 64, "\n", true))."\n";
//添加pem格式头和尾
$pubKey = "-----BEGIN PUBLIC KEY-----\n" . $key . "-----END PUBLIC KEY-----\n";
//转换为openssl格式密钥
$res = openssl_get_publickey($pubKey);
//调用openssl内置方法验签,返回bool值
$result = (bool)openssl_verify($unsign, $sign, $res);
file_put_contents("cardfun_login_log.txt", date("Y-m-d H:i:s")."||".$result.PHP_EOL,FILE_APPEND);
//释放资源
openssl_free_key($res);
//返回资源是否成功
if($result != 1){
$this->_message['err_msg']="sign签名验签失败";
$this->_message['err_code'] = "-1";
sendJson($this->_message);
exit();
}
}
?>