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

版权声明:转载请加链接 https://blog.csdn.net/qq_33722172/article/details/84872673

https://www.cnblogs.com/baocheng/p/5910448.html

先了解一下基本概念:

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

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

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

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

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

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

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

对称加密函数:

加密:string openssl_encrypt ( string $data , string $method , string $password) 其中 d a t a data为其要加密的数据, method是加密要使用的方法, p a s s w o r d 使 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 ] )

使用公匙加密数据,其中 d a t a data是要加密的数据; crypted是一个引用变量,加密后的数据会被放入这个变量中; k e y b i t 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 ] )

签名函数: d a t a data为要签名的数据; signature为签名结果的引用变量; p r i v k e y i d 使 ; priv_key_id为签名所使用的私匙; signature_alg为签名要使用的算法,其算法列表可以使用openssl_get_md_methods ()得到

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

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:验证失败 
}
}

测试签名验签:

$dn = array( 
"countryName" => 'XX', //所在国家名称 
"stateOrProvinceName" => 'State', //所在省份名称 
"localityName" => 'SomewhereCity', //所在城市名称 
"organizationName" => 'MySelf', //注册人姓名 
"organizationalUnitName" => 'Whatever', //组织名称 
"commonName" => 'mySelf', //公共名称 
"emailAddress" => '[email protected]' //邮箱 
); 
$test = new OpensslClass($dn);
$str = '这是一个验证签名测试文本';

$test->init();

$signtext = $test->sign($str);
echo '加密信息 :'.$signtext;
echo '<br/>';
echo '验证结果 :'.$test->verify($str,$signtext);

结果:

在这里插入图片描述

测试加密解密:

$test = new OpensslClass($dn);
$str = '这是一个验证加密解密测试文本';
$test->getPrivateKeyAndPublicKey();
$entext = $test->asymmetric_public_encrypt($str);
$detext = $test->asymmetric_private_decrypt($entext);
echo $entext.'<br />';
echo $detext;

结果:

在这里插入图片描述

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)打包成带公钥与私钥

猜你喜欢

转载自blog.csdn.net/qq_33722172/article/details/84872673
今日推荐