加密和摘要的区别
***摘要:是从已知的数据中,通过摘要计算出一个值,一个数据对应一个或多个摘要的值 ***
比如:md5 和 sha1 sha256 hash 就是得到一个特定的值 ,同一个数据得到的md5 是一样的,不会改变的
比如:password_hash 产生的是一个可以变化的摘要,同一个数据每一次生成的都是不一样的结果,当验证的时候可以使用password_verify 来验证是否正确
以下是上面的使用代码
$str = "hello huangjunhui";
var_dump(md5($str));
echo "<br/>";
var_dump(hash("md5",$str));
echo "<br/>";
var_dump(sha1($str));
echo "<br/>";
var_dump(hash("sha1",$str));
echo "<br/>";
var_dump(hash("sha256",$str));
echo "<br/>";
var_dump(hash("sha512",$str));
echo "<br/>";
这里说一下 hash 的用法, hash($algo,$data);
algo是散列的算法, 如果想要查看 hash 可以支持哪些算法, 可以使用 hash_algos() 函数, 得到的数组就是 hash 支持的算法
以上的摘要都是唯一值的, 同样还有一种算法不是唯一值的,比如我们使用的 password_hash 函数
$str = "hello huangjunhui";
$result = password_hash($str,PASSWORD_DEFAULT);
//得到加密的结果
echo $result."<br/>"; //$2y$10$4sFjS28dGe/DM5MQMQKPp.R.wWWqZYgHJQCHqGjo8vfPPKYrrBzl2
//$2y$10$uPBE9MKkJh1r4NSk5VgzduUhrgeMLgnJXzmbghEdh8cK3swnvAXEm
echo (int)password_verify($str,$result); //这里是一个布尔值,强转了int
上面可以看到, 每一次 password_hash的结果都是不一样的, 当验证的时候, 却总是结果为 真,所以这种方法,一般都用在存储用户的密码到数据库,就算数据库丢失,也不会让用户的密码泄漏
上面的几个摘要方法有一个共同点就是 不可逆,也就是说,你想通过密文再获取到原文是不可能的, 所以以上的算法都属于摘要算法
加密解密
能加密解密,就是说,我不仅可以从原文得到密文,也可以从密文在反过来得到原文
主要有两种方式, 一种是对称加密, 一种是非对称加密
php 的加解密用的 openssl_encrypt 和 openssl_decrype
顺便说一句, php之前有一个加密的方法 mcrypt 方法过时了
openssl 的使用注意事项
我在 windows 系统中使用的 openssl , 首先要在 php.ini中打开 openssl的扩展,同时还要找到openssl 的 配置文件(openssl.cnf)的路径,在会使用的时候要指明 openssl.cnf 的路径, 不然会报错
可以看到,phpinfo中的 openssl 的配置件在 /usr/local/ssl/openssl.cnf 路径中,window 中根本没有这个路径,所以我们在使用 php 的 openssl的时候要在配置中指明配置文件的路径
对称加密,就是加解密过程中,所使用的密钥是一样的
对称加密的算法, 通常有 AES 和 DES 两中,下面我们来看看 PHP 中的对称加密
对称加密通常用于内部的系统, 比如本地的文件加密, 加密时会产生一上 iv向量, 在进行网络传输时, 是要和密文一起传给接收方的, 双方密主要保证 密钥的安全, 不泄漏就可以了
以下是 生成一个密文, 再解密这个密文的代码
//生成一个密文,并返回 密文和iv向量
public function openssl(){
$str = "hello world,huang junhui"; //待加密的字符串
$key = openssl_random_pseudo_bytes(16); //这里相当于就是一个密钥了, 加密和解密时的密码必须是一样的才可以
dump(strlen($key)); //int(16)
$keybase64 = base64_encode($key); //因为上一步生成的 key是一个 二进制的数据, 显示出来是乱码, 所在要想显示,就要base64一下$key1 = base64_decode($keybase64);
dump($keybase64);
dump(strlen($keybase64)); //int(24)
$cipher = "AES-128-CBC"; //这里是加密算法的名称 可以使用 openssl_get_cipher_methods() 查看所有支持的加密算法名称
$ivLen = openssl_cipher_iv_length($cipher); //获取算法对应初始化向量的长度
$iv = openssl_random_pseudo_bytes($ivLen); //随机生成一个初始化向量 //这里也可以手写一个长度为 $ivLen 的字符串,此例中$ivLen是16, 所以可以随便写一个 ”sfasfasdfsdfasdf“
$encrypted = openssl_encrypt($str,$cipher,$key,OPENSSL_RAW_DATA,$iv);
//$encrypted = openssl_encrypt($str,$cipher,$keybase64,OPENSSL_RAW_DATA,"sfasfasdfsdfasdf"); //字符长度必需是16位的 $ivLen的长度
//这里使用 $key 和 $keybase64 不影响效果(长度不一样 一个是16一个是24), 也就是说 密钥的长度是可以任意长度的, 只要加密和解密的时侯使用同一个 key就可以了
//由于对称加密的特殊性, 当传输数据的时候,是要把iv明文一起传输线接收方的,所以当我们返回给请求方的时候要把 密文加上 $iv 一起传送给前端,又因为$iv是一个二进制数据,所以要base64一下,才可以在网络中传输
// dump(base64_encode($encrypted.":".$iv));
//接收端接到数据之后,首先base64_decode ,然后使用 ":" 把密文和向量数据分开
return base64_encode($encrypted.":::".$iv);
//$decrypted = openssl_decrypt($encrypted,$cipher,$key,OPENSSL_RAW_DATA,$iv);
//$decrypted = openssl_decrypt($encrypted,$cipher,$keybase64,OPENSSL_RAW_DATA,"sfasfasdfsdfasdf");
//dump($decrypted);
}
//解密密文
public function decodeopenssl(){
//传入的加密结果 "PDwc77SaX/WcpipdClDx3dYCQBjwWarDswmcwkYKUYU6OjqwKjMwKxcMKU34FYTRhqoj"
//加密时使用的 key "XHvcRREJw5vlJKP8AoR39A=="
//$base64str = input("secret_content");
$str = base64_decode("PDwc77SaX/WcpipdClDx3dYCQBjwWarDswmcwkYKUYU6OjqwKjMwKxcMKU34FYTRhqoj");
dump($str);
$secretArr = explode(":::",$str);
$secretbody = $secretArr[0];
$iv = $secretArr[1];
$key = base64_decode("XHvcRREJw5vlJKP8AoR39A==");
$cipher = "AES-128-CBC";
$decrypted = openssl_decrypt($secretbody,$cipher,$key,OPENSSL_RAW_DATA,$iv);
dump($decrypted);
}
对称加密,是双方都知道密钥的情况下,使用的,最主要的是密钥的安全,iv 明文传输没关系
非对称加密
非对称加密可以用作对数据的加密, 也可以用作对数据的签名;
加密和签名的理解, 加密的时候是 返回给接收方一段无序的数据, 要对方解密才可以使用,签名是, 把原数据给接收方,并在返回的数据中 带上一个由原数据生成的一个签名数据
public function openssl(){
$str = "hello world,huang junhui"; //待加密的字符串
//上面说过,在windows中,openssl 的配置文件的路径是要自己找定的 系统默认的路径不是正确的,所以我们要进 配置文的的路径的配置
//两种方式, 一种是在系统环境变量中配置 环境变量名:OPENSSL_CONF 变量值 :就是openssl.cnf文件的位置 D:\laragon\bin\apache\httpd-2.4.54-win64-VS16\conf\openssl.cnf
//另一种方式,在config数组中配置 "config" =>"D:\laragon\bin\apache\httpd-2.4.54-win64-VS16\conf\openssl.cnf" , 注意config 的结构
$config = [
"config"=>"D:\laragon\bin\apache\httpd-2.4.54-win64-VS16\conf\openssl.cnf",
"digest_alg" => "sha512", //摘要算法或签名的算法 非必填
"private_key_bits" => 2048, //私钥的长度 512 1024 2048 4096 通常选1024或2048
"private_key_type" => OPENSSL_KEYTYPE_RSA, //加密类型 通常可以选择OPENSSL_KEYTYPE_DSA、 OPENSSL_KEYTYPE_DH、 OPENSSL_KEYTYPE_RSA 或 OPENSSL_KEYTYPE_EC。 默认值是 OPENSSL_KEYTYPE_RSA。
];
//创建私钥和公钥资源
$res = openssl_pkey_new($config); //注意这里是一个包含了公私钥的资源
//资源中获取私钥 公私钥资源 生成的私钥 第三个参数是可以加一些盐值 第四个参数是 上面的配置项
openssl_pkey_export($res, $private_key, null, $config);
//dump($private_key);
//从资源中获取公钥数组信息
$public_key_arr = openssl_pkey_get_details($res);
$public_key = $public_key_arr["key"];
dump($public_key);
//这样我们就生成了一对公私钥, 我们可以把每个生成的公私钥保存到数据库,得到一个数据库的编号,然后把公钥数据传给请求方,请求方使用公钥加密数据之后, 再返回给服务器, 服务器使用私钥解密
//签名 //签名是用公钥签名,使用私钥来验证签名
openssl_sign($str,$sign,$private_key,OPENSSL_ALGO_SHA1);
dump(openssl_verify($str,$sign,$public_key,OPENSSL_ALGO_SHA1));
//有了上面的公钥,我们就可以用来加密、
openssl_public_encrypt($str,$encrypted_data,$public_key);
dump($encrypted_data);
//解密数据
openssl_private_decrypt($encrypted_data,$decrypted_data,$private_key);
dump($decrypted_data);
}
以上就是openssl 的用法
下面是一个 封装的 RSA 类 , 可以做参考
<?php
/**
* RSA签名类
*/
class Rsa
{
public $publicKey = '';
public $privateKey = '';
private $_privKey;
/**
* * private key
*/
private $_pubKey;
/**
* * public key
*/
private $_keyPath;
/**
* * the keys saving path
*/
/**
* * the construtor,the param $path is the keys saving path
* @param string $publicKey 公钥
* @param string $privateKey 私钥
*/
public function __construct($publicKey = null, $privateKey = null)
{
$this->setKey($publicKey, $privateKey);
}
/**
* 设置公钥和私钥
* @param string $publicKey 公钥
* @param string $privateKey 私钥
*/
public function setKey($publicKey = null, $privateKey = null)
{
if (!is_null($publicKey)) {
$this->publicKey = $publicKey;
}
if (!is_null($privateKey)) {
$this->privateKey = $privateKey;
}
}
/**
* * setup the private key
*/
private function setupPrivKey()
{
if (is_resource($this->_privKey)) {
return true;
}
$pem = chunk_split($this->privateKey, 64, "\n");
$pem = "-----BEGIN PRIVATE KEY-----\n" . $pem . "-----END PRIVATE KEY-----\n";
$this->_privKey = openssl_pkey_get_private($pem);
return true;
}
/**
* * setup the public key
*/
private function setupPubKey()
{
if (is_resource($this->_pubKey)) {
return true;
}
$pem = chunk_split($this->publicKey, 64, "\n");
$pem = "-----BEGIN PUBLIC KEY-----\n" . $pem . "-----END PUBLIC KEY-----\n";
$this->_pubKey = openssl_pkey_get_public($pem);
return true;
}
/**
* * encrypt with the private key
*/
public function privEncrypt($data)
{
if (!is_string($data)) {
return null;
}
$this->setupPrivKey();
$r = openssl_private_encrypt($data, $encrypted, $this->_privKey);
if ($r) {
return base64_encode($encrypted);
}
return null;
}
/**
* * decrypt with the private key
*/
public function privDecrypt($encrypted)
{
if (!is_string($encrypted)) {
return null;
}
$this->setupPrivKey();
$encrypted = base64_decode($encrypted);
$r = openssl_private_decrypt($encrypted, $decrypted, $this->_privKey);
if ($r) {
return $decrypted;
}
return null;
}
/**
* * encrypt with public key
*/
public function pubEncrypt($data)
{
if (!is_string($data)) {
return null;
}
$this->setupPubKey();
$r = openssl_public_encrypt($data, $encrypted, $this->_pubKey);
if ($r) {
return base64_encode($encrypted);
}
return null;
}
/**
* * decrypt with the public key
*/
public function pubDecrypt($crypted)
{
if (!is_string($crypted)) {
return null;
}
$this->setupPubKey();
$crypted = base64_decode($crypted);
$r = openssl_public_decrypt($crypted, $decrypted, $this->_pubKey);
if ($r) {
return $decrypted;
}
return null;
}
/**
* 构造签名
* @param string $dataString 被签名数据
* @return string
*/
public function sign($dataString)
{
$this->setupPrivKey();
$signature = false;
openssl_sign($dataString, $signature, $this->_privKey);
return base64_encode($signature);
}
/**
* 验证签名
* @param string $dataString 被签名数据
* @param string $signString 已经签名的字符串
* @return number 1签名正确 0签名错误
*/
public function verify($dataString, $signString)
{
$this->setupPubKey();
$signature = base64_decode($signString);
$flg = openssl_verify($dataString, $signature, $this->_pubKey);
return $flg;
}
public function __destruct()
{
is_resource($this->_privKey) && @openssl_free_key($this->_privKey);
is_resource($this->_pubKey) && @openssl_free_key($this->_pubKey);
}
}
$publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKZ1mKTymRoGKnHiP1xAy4aiyt5r0BscCZnDAonCrMFZ4kBGriPNHxEaLr5lfBnMKw7k6i+2dsFPSEZooTvqtPUCAwEAAQ==';
$privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEApnWYpPKZGgYqceI/XEDLhqLK3mvQGxwJmcMCicKswVniQEauI80fERouvmV8GcwrDuTqL7Z2wU9IRmihO+q09QIDAQABAkBunx3nGHXYjppsfn++7iyTd+I7+Agfy/0xWyB3rpEiGGgfemjcRFaeq5SC2vUNXsrEOY5gbUSQmFxH//Cym18NAiEA1z1cZx/Q9cbIjFPwp1a+K5CVFDXDcfbi/AQgAkVs0/cCIQDF+2fr23AoBslcOC4S0yAx94AbgxCntYuRqztxybsrcwIgMW86ZcT87TX2oaQ1xXk6vC68zqN6fBZEE7Wu1Fa1pAkCIElmOJP3qfAc/AAlj+dIwLHlqWgJwl3674CU9Bfui2bDAiEA0CKJpF8x7KANCcopEQC93PsbIztuML322LOfDV1Lw/k=';
$rsa=new Rsa($publicKey,$privateKey);
$str="abc";
echo "原始数据:".$str;
echo "<br/><hr>";
$res=$rsa->privEncrypt($str);
echo "私钥加密数据:".$res;
echo "<br/>";
$res2=$rsa->pubDecrypt($res);
echo "公钥解密数据:".$res2;
echo "<br/><hr>";
$res3=$rsa->pubEncrypt($str);
echo "公钥加密数据:".$res3;
echo "<br/>";
$res4=$rsa->privDecrypt($res3);
echo "私钥解密数据:".$res4;
echo "<br/><hr>";
echo "签名数据:".$str;
$res5=$rsa->sign($str);
echo "<br/>";
echo "签名结果:".$res5;
$res6=$rsa->verify($str,$res5);
echo "<br/>";
echo "验证签结果:".$res6;