php openssl扩展学习以及基本使用

先了解一下基本概念:

公钥/私钥/签名/验证签名/加密/解密/对称加密/非对称加密

公钥与私钥是通过一种算法得到的一个密钥对(即一个公钥和一个私钥),公钥是密钥对中公开的部分,私钥则是非公开的部分。公钥通常用于加密会话密钥、验证数字签名,或加密可以用相应的私钥解密的数据.

当然,公钥和私钥都可以用来加密数据,用另一个解开。这里有两种情况,公钥加密私钥解密的情况被称为加密解密;私钥加密数据,公钥解密一般被称为签名和验证签名.

公钥和私钥是一一对应的,公钥加密的数据只有它相对应的私钥可以解开,当你想跟A通信时,和A交换公钥,你要发给A的数据就用A的公钥加密,他收到数据后用自己的私钥解密,同理A发送消息时也是这种操作,这样最大程度保证了安全性。

但是我们如何知道和我沟通的就真的是A吗?所以就要用上签名,用自己的私钥对数据进行签名发送给A,这个数据就只有配对的公钥可以解开,而这个私钥只有我有,所以如果配对的公钥解开了数据,就说明这数据是我发的,相反,则不是.这个被称为签名,A用我的公钥解密数据叫做验证签名。

加密时用一个密码加密文件,然后解密也用同样的密码,这个是对称加密。而有些加密时,加密用的一个密码,而解密用另外一组密码,这个叫非对称加密,意思就是加密解密的密码不一样

openssl扩展提供了很多的函数,这里介绍一下常用的函数:

对称加密函数:

加密:string openssl_encrypt ( string $data , string $method , string $password) 其中data为其要加密的数据,data为其要加密的数据,data为其要加密的数据,method是加密要使用的方法,password是要使用的密匙,函数返回加密后的数据,password是要使用的密匙,函数返回加密后的数据,password是要使用的密匙,函数返回加密后的数据,method列表可以使用openssl_get_cipher_methods()来获取

解密:string openssl_encrypt ( string $data , string $method , string $password)

非对称加密函数:

openssl_get_publickey(); 别名openssl_pkey_get_public(); // 从证书导出公匙;
openssl_get_privatekey(); 别名openssl_pkey_get_private(); // 从证书导出私匙;

openssl_public_encrypt(string $data , string &$crypted , mixed $key [, int $padding = OPENSSL\_PKCS1\_PADDING ] )
使用公匙加密数据,其中data是要加密的数据;data是要加密的数据;data是要加密的数据;crypted是一个引用变量,加密后的数据会被放入这个变量中;key是要传入的公匙数据;由于被加密数据分组时,有可能不会正好为加密位数bit的整数倍,所以需要key是要传入的公匙数据;由于被加密数据分组时,有可能不会正好为加密位数bit的整数倍,所以需要key是要传入的公匙数据;由于被加密数据分组时,有可能不会正好为加密位数bit的整数倍,所以需要padding(填充补齐),$padding的可选项有 OPENSSL_PKCS1_PADDING, OPENSSL_NO_PADDING,分别为PKCS1填充,或不使用填充;

与此方法相对的还有(传入参数一致):

openssl_private_encrypt(); // 使用私匙加密;
openssl_private_decrypt(); // 使用私匙解密;
openssl_public_decrypt(); // 使用公匙解密;
签名和验签函数:

bool openssl_sign ( string $data , string &$signature , mixed $priv_key_id [, mixed $signature_alg = OPENSSL_ALGO_SHA1 ] )
int openssl_verify ( string $data , string $signature , mixed $pub_key_id [, mixed $signature_alg = OPENSSL_ALGO_SHA1 ] )

 

 

这里我针对上述常用函数封装了一个加密解密类,实现了基本的加密解密签名验签功能,代码如下:

class OpensslClass {


private $dn;
private $privkeypass = '111111'; //私钥密码
private $numberofdays = 365; //有效时长
private $cerpath = "./sign/test.cer"; //生成证书路径
private $pfxpath = "./sign/test.pfx"; //密钥文件路径
private $prikeypath = "./key/privkey.pem"; //私钥文件
private $pubkeypath = "./key/pubkey.key"; //公钥文件
public function __construct(array $dn)
{
$this->dn = $dn;
}
/**
* 对称加密
* @param string 明文
* @param string key
* @param string 加密方式
* @return array [密文, 伪随机字节串]
*/
public function symmetric_encrypt($plaintext, $key, $cipher = 'aes-128-cbc')
{
if (in_array($cipher, openssl_get_cipher_methods()))//判断传递的加密算法是否在可用的加密算法的列表中
{
$ivlen = openssl_cipher_iv_length($cipher); //获取密码初始化向量(iv)长度
$iv = openssl_random_pseudo_bytes($ivlen); //生成一个伪随机字节串 string ,字节数由 length 参数指定
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv);
return [$ciphertext,$iv];
} else {
return '加密方式传递错误';
}
}
//对称解密
/**
* 对称解密
* @param string $ciphertext 密文
* @param string $key 密钥
* @param string $iv 伪随机字节串
* @param string $cipher 加密方式
* @return string 明文
*/
public function symmetric_decrypt($ciphertext, $key, $iv, $cipher = 'aes-128-cbc')
{
if (in_array($cipher, openssl_get_cipher_methods()))
{
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv);
return $original_plaintext;
} else {
return '加密方式传递错误';
}
}
/**
* 获取私钥公钥
* @param array $configargs 配置
* @return bool [description]
*/
public function getPrivateKeyAndPublicKey($configargs = array())
{

if (empty($configargs)) {
$configargs = array(
'private_key_bits' => 1024, // Size of Key.
'private_key_type' => OPENSSL_KEYTYPE_RSA
);
}
//$res返回false的时候,检查发现,是window系统缺少了openssl环境变量,解决方法如下:
$opensslConfigPath = "D:/phpstudy/PHPTutorial/Apache/conf/openssl.cnf"; //apache路径下的openssl.conf文件路径
$res = openssl_pkey_new($configargs);////生成一个新的私钥 openssl_pkey_new ([ array $configargs ] ) configargs参数微调密钥的生成(比如private_key_bits 指定应该使用多少位来生成私钥)
if(!$res) {
$configargs['config'] = $opensslConfigPath;
$res = openssl_pkey_new($configargs);
}
openssl_pkey_export($res, $privKey, null, $configargs);//将一个密钥的可输出表示转换为字符串
$file = fopen($this->prikeypath, 'w');
fwrite($file, trim($privKey));
fclose($file);
$pubkey=openssl_pkey_get_details($res);
$pubkey=$pubkey["key"];
$file = fopen($this->pubkeypath, 'w');
fwrite($file, trim($pubkey));
fclose($file);
return true;
}
//非对称私钥加密
public function asymmetric_private_encrypt($plaintext)
{
$pkey = openssl_pkey_get_private(file_get_contents($this->prikeypath));
openssl_private_encrypt($plaintext,$crypttext,$pkey);
$crypttext = base64_encode($crypttext);//加密后的内容通常含有特殊字符,需要编码转换下,在网络间通过url传输时要注意base64编码是否是url安全的
return $crypttext;
}
//非对称公钥解密
public function asymmetric_public_decrypt($crypttext)
{
$pubkey = file_get_contents($this->pubkeypath);
$res = openssl_pkey_get_public($pubkey);
openssl_public_decrypt(base64_decode($crypttext), $decrypttext, $res);
return $decrypttext;
}
//非对称公钥加密
public function asymmetric_public_encrypt($plaintext)
{
$pkey = openssl_pkey_get_public(file_get_contents($this->pubkeypath));
openssl_public_encrypt($plaintext,$crypttext,$pkey);
$crypttext = base64_encode($crypttext);//加密后的内容通常含有特殊字符,需要编码转换下,在网络间通过url传输时要注意base64编码是否是url安全的
return $crypttext;
}
//非对称私钥解密
public function asymmetric_private_decrypt($crypttext)
{
$pubkey = file_get_contents($this->prikeypath);
$res = openssl_pkey_get_private($pubkey);
openssl_private_decrypt(base64_decode($crypttext), $decrypttext, $res);
return $decrypttext;
}
//证书生成
public function getCertificate()
{
$configargs = array(
'config' => "D:/phpstudy/PHPTutorial/Apache/conf/openssl.cnf",
'private_key_bits' => 1024, // Size of Key.
'private_key_type' => OPENSSL_KEYTYPE_RSA
);
//生成证书
$privkey = openssl_pkey_new($configargs); //生成一个新的私钥 openssl_pkey_new ([ array $configargs ] ) configargs参数微调密钥的生成(比如private_key_bits 指定应该使用多少位来生成私钥)
$csr = openssl_csr_new($this->dn, $privkey,$configargs); //根据dn提供的信息生成新的CSR(证书签名请求) privkey 应该被设置为由openssl_pkey_new()函数预先生成(或者以其他方式从openssl_pkey函数集中获得)的私钥。该密钥的相应公共部分将用于签署CSR.
$sscert = openssl_csr_sign($csr, null, $privkey, $this->numberofdays,$configargs); //用另一个证书签署 CSR (或者本身) 并且生成一个证书 从给定的 CSR 生成一个x509证书资源
openssl_x509_export($sscert, $csrkey); //导出证书$csrkey 将 x509 以PEM编码的格式导出到名为 output 的字符串类型的变量中 公钥证书 只有公钥
openssl_pkcs12_export($sscert, $privatekey, $privkey, $this->privkeypass); //导出密钥$privatekey
//生成证书文件
$fp = fopen($this->cerpath, "w");
fwrite($fp, $csrkey);
fclose($fp);
//生成密钥文件
$fp = fopen($this->pfxpath, "w");
fwrite($fp, $privatekey);
fclose($fp);
return true;
}
/**
* 签名
* @param string $data 明文
* @return string 加密信息
*/
public function sign($data)
{
$priv_key = file_get_contents($this->pfxpath); //获取密钥文件内容
//私钥加密
openssl_pkcs12_read($priv_key, $certs, $this->privkeypass); //读取公钥、私钥
$prikeyid = $certs['pkey']; //私钥
openssl_sign($data, $signMsg, $prikeyid,OPENSSL_ALGO_SHA1); //注册生成加密信息
$signMsg = base64_encode($signMsg); //base64转码加密信息 加密后的内容通常含有特殊字符,需要编码转换下,在网络间通过url传输时要注意base64编码是否是url安全的
return $signMsg;
}
/**
* 公钥验证签名
* @param string $data 明文
* @param string $signMsg 加密信息
* @return bool 是否验证通过
*/
public function verify($data,$signMsg)
{
$priv_key = file_get_contents($this->pfxpath);
//公钥解密
$unsignMsg=base64_decode($signMsg);//base64解码加密信息
openssl_pkcs12_read($priv_key, $certs, $this->privkeypass); //读取公钥、私钥
$pubkeyid = $certs['cert']; //公钥
$res = openssl_verify($data, $unsignMsg, $pubkeyid); //验证
return $res; //输出验证结果,1:验证成功,0:验证失败
}
}
测试签名验签:

 

 

 

结果:

 

测试加密解密:


结果:

 

openssl还提供了很多函数,其中涉及了几个名词,pkcs12,pkcs7,x509,围绕这几个名词提供了许多函数,我在网上查阅了一些博客,这里介绍一下它们的含义作用以及区别:

x509,公钥证书,只有公钥。
pkcs7,签名或加密。可以往里面塞x509,同时没有签名或加密内容。
pkcs12,含有私钥,同时可以有公钥,有口令保护。
pkcs7的作用就是电子信封。
X509是基本规范
pkcs7和pkcs12是两个实现规范,pkcs7是数字信封,pkcs12是带有私钥的证书规范。
x509是数字证书的规范,pkcs7和pkcs12是两种封装形式。比如说同样的电影,有的是avi格式,有的是mpg,大概就这个意思。

pkcs7一般是把证书分成两个文件,一个公钥一个私钥,有PEM和DER两种编码方式。PEM比较多见,就是纯文本的,pkcs7一般是分发公钥用,看到的就是一串可见字符串,扩展名经常是.crt,.cer,.key等。DER是二进制编码。
pkcs12是把证书压成一个文件,.pfx 。主要是考虑分发证书,私钥是要绝对保密的,不能随便以文本方式散播。所以pkcs7格式不适合分发。.pfx中可以加密码保护,所以相对安全些。
在实践中要中,用户证书都是放在USB Key中分发,服务器证书经常还是以文件方式分发。服务器证书和用户证书,都是X509证书,就是里面的属性有区别。

X509 是证书规范
pkcs7 是消息语法 (常用于数字签名与加密)
pkcs12 个人消息交换与打包语法 (如.PFX .pkcs12)打包成带公钥与私钥

posted @ 2020-06-07 12:58  年少有为AAA  阅读(416)  评论(0编辑  收藏  举报