PHP常用加密算法

md5

md5 ($string, $binary = false)

binary = false时返回的结果是32位的二进制数据
binary = true时返回的结果是16位的二进制数据

sha1

sha1 ($string, $binary = false)

binary = false时返回的结果是32位的二进制数据
binary = true时返回的结果是16位的二进制数据

password_hash

这个函数是PHP 5.5.0新特性。用于密码加密。我们在开发中经常使用md5进行加密,通常是原密码加密码盐的方式。
password_hash()用于加密更加安全。
string password_hash ( string $password , integer $algo [, array $options ]);
前两个参数为必填,后一个参数可选。例如:

$encrypted = password_hash('password123', PASSWORD_BCRYPT, ['cost'=>12]);
//$encrypted = $2y$12$ADGJRSCJGn3it43JAopjp.ewtYO4RtIBvCpSAtY8sGBks9zYj2dHW
//$2y 为PASSWORD_BCRYPT的值  $12 为 cost的值 后面是加密后的值
$verify = password_verify('password123', $encrypted);	//true
$verify2 = password_verify('password1236', $encrypted);	//false

其中第三个参数是PASSWORD_BCRYPT算法的选项值,cost是指算法使用的递归层数,默认是10.层数越高越复杂,但对硬件的要求越高,默认是10已经够用。该模式算法还有salt选项,即密码盐,使用它也可以增大密码的强度,但是需要注意的是该选项在php7.0版本中已经废除。
password_verify()用于对password_hash()加密后的密码进行校验
password_verify ( string $password , string $hash )
该函数有两个参数,第一个是加密前的值,第二个是加密后的值,返回bool类型。

使用password_hash()进行加密需要注意项目使用的php版本。还有一点就是多种编程语言用同一密码时最好不要用这种加密方法,因为password_verify()加密方法是php特有的,其他语言未必有相同的解密函数。

字符串编码

urlencode

客户端在进行网页请求的时候,网址中可能会包含非ASCII码形式的内容,比如中文。而直接把中文放到网址中请求是不允许的,所以需要用URLEncoder编码地址,将网址中的非ASCII码内容转换成可以传输的字符。

原理
将需要转换的内容(ASCII码形式之外的内容),用十六进制表示法转换出来,并在之前加上%开头。内容中的空格‘ ’ ,全部用+代替。

$encrypted = urlencode('http://baidu.com?a=a&b=bb&c=ccc');	//http%3A%2F%2Fbaidu.com%3Fa%3Da%26b%3Dbb%26c%3Dccc
$decrypted = urldecode($encrypted);	//http://baidu.com?a=a&b=bb&c=ccc

json_encode

json_encode() 用于对变量进行 JSON 编码,该函数如果执行成功返回 JSON 数据,否则返回 FALSE
string json_encode ( $value [, $options = 0 ] )
参数
value: 要编码的值。该函数只对 UTF-8 编码的数据有效。
options:由以下常量组成的二进制掩码 JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT, JSON_PRESERVE_ZERO_FRACTION, JSON_UNESCAPED_UNICODE, JSON_PARTIAL_OUTPUT_ON_ERROR。
要注意的是 JSON_UNESCAPED_UNICODE 选项,如果我们不希望中文被编码,可以添加该选项。

$arr = array('cn' => '中文', 'en' => 'english');
$str1 = json_encode($arr);
var_dump($str1);	//string(36) "{"cn":"\u4e2d\u6587","en":"english"}"
var_dump(json_decode($str1));	
//object(stdClass)#3816 (2) {
//  ["cn"]=>
//  string(6) "中文"
//  ["en"]=>
//  string(7) "english"
//}
$str2 = json_encode($arr, JSON_UNESCAPED_UNICODE);
var_dump($str2);	//string(30) "{"cn":"中文","en":"english"}"
var_dump(json_decode($str2, true));
//array(2) {
//  ["cn"]=>
//  string(6) "中文"
//  ["en"]=>
//  string(7) "english"
//}

serialize

serialize函数用于序列化对象或数组,并返回一个字符串

$sites = array('Google', 'Runoob', 'Facebook');
$serialized_data = serialize($sites);	//a:3:{i:0;s:6:"Google";i:1;s:6:"Runoob";i:2;s:8:"Facebook";}
$sites = unserialize($serialized_data);	//array('Google', 'Runoob', 'Facebook')
<?php
class MyClass1 { 
   public $obj1prop;   
}
class MyClass2 {
   public $obj2prop;
}


$obj1 = new MyClass1();
$obj1->obj1prop = 1;
$obj2 = new MyClass2();
$obj2->obj2prop = 2;

$serializedObj1 = serialize($obj1);
$serializedObj2 = serialize($obj2);

// 默认行为是接收所有类
// 第二个参数可以忽略
// 如果 allowed_classes 设置为 true, unserialize 会将所有对象转换为 __PHP_Incomplete_Class 对象
$data = unserialize($serializedObj1 , ["allowed_classes" => true]);

ini_set('unserialize_callback_func', 'mycallback'); // set your callback_function
function mycallback($classname) 
{
    // just include a file containing your class definition
    // you get $classname to figure out which class definition is required
}

// 转换所有对象到 __PHP_Incomplete_Class 对象,只允许 MyClass1 和 MyClass2 转换到 __PHP_Incomplete_Class
$data2 = unserialize($serializedObj2 , ["allowed_classes" => ["MyClass1", "MyClass2"]]);

print($data->obj1prop);	//1
print(PHP_EOL);
print($data2->obj2prop);//2
?>

base64

原理

Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个为一个单元,对应某个可打印字符。

三个bites有24个比特,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。它可用来作为电子邮件的传输编码。

在Base64中的可打印字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。

如在mime(多用途邮件扩展)中,Base64的使用的64个可打印字符

A-Za-z:大小写字母各26个

0-9:加上10个数字

+:加号

/:斜杠

一共64个字符,等号“=”用来作为后缀用途

对应的转换关系为

0-63:A-Za-z0-9+/

转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲器中剩下的bit用0补足。然后,每次取出6(因为26=64)个bit,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。

当原数据长度不是3的整数倍时, 如果最后剩下一个输入数据,在编码结果后加2个“=”;如果最后剩下两个输入数据,编码结果后加1个“=”;如果没有剩下任何数据,就什么都不要加,这样才可以保证数据还原的正确性。

PHP应用

$encrypted = base64_encode('This is test');	//VGhpcyBpcyB0ZXN0
$decrypted = base64_decode($encrypted);	//This is test

AES

加密流程

高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图:
AES加密流程

加密原理

按加密方式分为:AES-128、AES-192、AES-256;
按加密模式分为:ECB、CBC、CFB、OFB、CTR、PCBC等。

AES的基本结构

AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。密钥的长度不同,推荐加密轮数也不同,如下表所示:

加密方式密钥长度(字节)分组长度(字节)加密轮数
AES-128161610
AES-192241612
AES-256321614

备注:php中不同的编码格式下字符占用的字节是不同的。ANSI编码中文字符占2个字节、英文字符占1字节;UTF-8编码中文字符占3个字节、英文字符占1字节;Unicode编码中文字符占2个字节、英文字符占2字节。

加密模式

  • ECB
    ECB模式是最早采用和最简单的模式,它将加密的数据分成若干组,每组的大小跟加密密钥长度相同,然后每组都用相同的密钥进行加密。
    优点:有利于并行计算;误差不会累计(互不干扰)。
    缺点:可能对明文进行主动攻击。

  • CBC
    CBC模式对于每个待加密的密码块,在加密前会先与前一个密码块的密文异或然后再用加密器加密(图中的圆圈十字符号表示异或操作,下同)。第一个明文块与一个叫初始化向量的数据块异或。加、解密双方共同知晓密钥和初始化向量才能实现加解密。
    优点:安全性比ECB模式高;是SSL的标准。
    缺点:数据块之间的加密有依赖关系,因此不能并行计算。

  • CFB
    CFB 模式是用分组算法实现流算法,明文数据不需要按分组大小对齐。
    优点:明文数据不需要按分组大小对其,即无需填充。
    缺点:同CBC模式,无法并行计算。

  • OFB
    OFB 模式的过程和CBC模式有点像,但明文数据不需要按分组大小对齐。
    优点:明文数据不需要按分组大小对其,即无需填充。
    缺点:同CBC模式,无法并行计算。

  • CTR
    CTR模式是在ECB模式的基础上,引入了Nonce随机数和Counter计数器,Nounce随机数和Counter计数器整体可看作计数器,每加密一段明文,计数器向上加一,并且这个计数器都会和初始IV进行连接、加加、异或等运算,然后使用加密器进行加密,最后在和明文异或得到分段密文。
    优点:明文数据不需要按分组大小对其,即无需填充。
    缺点:加密方和解密方需要同时维护初始IV、Nonce、Counter。

  • PCBC
    PCBC模式是CBC模式的改进版,与CBC模式的不同点在于,CBC模式后段明文加密的所需向量是前一段的密文,而PCBC模式后段明文加密所需的向量是前一段明文和密文的异或值。
    优点:同CBC模式。
    缺点:同CBC模式。

PHP应用

PHP函数开始版本 >=弃用版本 <依赖扩展
mcrypt_encrypt / mcrypt_decrypt4.0.27.2.0mcrypt
openssl_encrypt / openssl_decrypt5.3.0-openssl
<?php
// openssl_encrypt 函数实现示例
class AES
{
	const METHOD = 'AES-256-CBC';	//加密模式
	
    //加密
    public static function encrypt($data, $key, $iv){
        return base64_encode(openssl_encrypt($data, self::METHOD, $key, OPENSSL_RAW_DATA, md5($iv, true)));
    }
    //iv不是16个字节会报Warning
    //解密
    public static function decrypt($data, $key, $iv){
        return openssl_decrypt(base64_decode($data), self::METHOD, $key, OPENSSL_RAW_DATA, md5($iv, true));
    }
}

$text = 'This is test';
$key = 'bnZpc2libGU9JmZp13credivh4tQhAES';	//AES-256最多取前32个字节
$iv = 'pc2lib13credh4tQ';

$encrypted = AES::encrypt($text, $key, $iv);
var_dump($encrypted);	//0YjQ+Fa2Ebuobx8YOGOiLQ==

$decrypted =AES::decrypt($encrypted, $key, $iv);
var_dump($decrypted);	//This is test
die;

RSA

RSA算法属于非对称加密算法,非对称加密算法需要两个秘钥:公开密钥(publickey)和私有秘钥(privatekey).公开密钥和私有秘钥是一对,

如果公开密钥对数据进行加密,只有用对应的私有秘钥才能解密;

如果私有秘钥对数据进行加密那么只有用对应的公开密钥才能解密.
需要注意的地方

1.RSA 加密或签名后的结果是不可读的二进制,使用时经常会转为 BASE64 码再传输

2.RSA 加密时,对要加密数据的大小有限制,最大不大于密钥长度。例如在使用 1024 bit 的密钥时(秘钥生成可以自行百度),最大可以加密 1024/8=128 Bytes 的数据。数据大于 128 Bytes 时,需要对数据进行分组加密(如果数据超限,加解密时会失败,openssl 函数会返回 false),分组加密后的加密串拼接成一个字符串后发送给客户端。

为了保证每次加密的结果都不同,RSA 加密时会在待加密数据后拼接一个随机字符串,再进行加密。不同的填充方式 Padding 表示这个字符串的不同长度,在对超限数据进行分组后,会按照这个 Padding 指定的长度填入随机字符串。例如如果 Padding 填充方式使用默认的 OPENSSL_PKCS1_PADDING(需要占用 11 个字节用于填充),那么明文长度最多只能就是 128-11=117 Bytes。

接收方解密时也需要分组。将加密后的原始二进制数据(对于经过 BASE64 的数据,需要解码),每 128 Bytes 分为一组,然后再进行解密。解密后,根据 Padding 的长度丢弃随机字符串,把得到的原字符串拼接起来,就得到原始报文。

3.openssl_public_encrypt函数 php的默认填充和无填充是有区别的,如果只是php和php对接则不需要关注这个问题,如果是php跟c或java,需要选择无填充然后自行加入填充

4.需要将php的openssl模块打开或安装(win上是打开,linux上是安装,具体自行百度)
下面使用的相关函数功能介绍:

openssl_pkey_get_public() 从证书中提取公钥
openssl_pkey_get_private() 从证书中提取私钥
openssl_public_encrypt() 公钥加密
openssl_private_decrypt() 私钥解密
openssl_private_encrypt() 私钥加密
openssl_public_decrypt() 公钥解密
base64_encode() 使用base64对数据重新编码
base64_decode() 将base64的数据解码

//公钥加密 
$public_key = openssl_pkey_get_public(RSA_PUBLIC); 
if(!$public_key){
    die('公钥不可用');
}
//第一个参数是待加密的数据只能是string,第二个参数是加密后的数据,第三个参数是openssl_pkey_get_public返回的资源类型,第四个参数是填充方式
$return_en = openssl_public_encrypt("hello world", $crypted, $public_key);
if(!$return_en){
    return('加密失败,请检查RSA秘钥');
}
$eb64_cry = base64_encode($crypted);
echo "公钥加密数据:".$eb64_cry;
echo "<hr>";

//私钥解密
$private_key = openssl_pkey_get_private(RSA_PRIVATE);
if(!$private_key){
    die('私钥不可用');
}
$return_de = openssl_private_decrypt(base64_decode($eb64_cry), $decrypted, $private_key);
if(!$return_de){
    return('解密失败,请检查RSA秘钥');
}
echo "私钥解密数据:".$decrypted;
echo "<hr>";
//私钥加密
$private_key = openssl_pkey_get_private(RSA_PRIVATE);
if(!$private_key){
    die('私钥不可用');
}
$return_en = openssl_private_encrypt("hello world222222", $crypted, $private_key);
if(!$return_en){
    return('加密失败,请检查RSA秘钥');
}
$eb64_cry = base64_encode($crypted);
echo "私钥加密数据".$eb64_cry;
echo "<hr>";

//公钥解密
$public_key = openssl_pkey_get_public(RSA_PUBLIC);
if(!$public_key){
    die('公钥不可用');
}
$return_de = openssl_public_decrypt(base64_decode($eb64_cry), $decrypted, $public_key);
if(!$return_de){
    return('解密失败,请检查RSA秘钥');
}
echo "公钥解密数据:".$decrypted;
echo "<hr>";

DES

DES 是对称性加密里面常见一种,全称为 Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法。密钥长度是64位(bit),超过位数密钥被忽略。所谓对称性加密即加密和解密密钥相同,对称性加密一般会按照固定长度,把待加密字符串分成块,不足一整块或者刚好最后有特殊填充字符。

跨语言做 DES 加密解密经常会出现问题,往往是填充方式不对、编码不一致或者加密解密模式没有对应上造成。常见的填充模式有: pkcs5、pkcs7、iso10126、ansix923、zero。加密模式有:DES-ECB、DES-CBC、DES-CTR、DES-OFB、DES-CFB。

<?php

/**
 * openssl 实现的 DES 加密类,支持各种 PHP 版本
 */
class DES
{
    /**
     * @var string $method 加解密方法,可通过 openssl_get_cipher_methods() 获得
     */
    protected $method;

    /**
     * @var string $key 加解密的密钥
     */
    protected $key;

    /**
     * @var string $output 输出格式 无、base64、hex
     */
    protected $output;

    /**
     * @var string $iv 加解密的向量
     */
    protected $iv;

    /**
     * @var string $options
     */
    protected $options;

    // output 的类型
    const OUTPUT_NULL = '';
    const OUTPUT_BASE64 = 'base64';
    const OUTPUT_HEX = 'hex';


    /**
     * DES constructor.
     * @param string $key
     * @param string $method
     *      ECB DES-ECB、DES-EDE3 (为 ECB 模式时,$iv 为空即可)
     *      CBC DES-CBC、DES-EDE3-CBC、DESX-CBC
     *      CFB DES-CFB8、DES-EDE3-CFB8
     *      CTR
     *      OFB
     *
     * @param string $output
     *      base64、hex
     *
     * @param string $iv
     * @param int $options
     */
    public function __construct($key, $method = 'DES-ECB', $output = '', $iv = '', $options = OPENSSL_RAW_DATA | OPENSSL_NO_PADDING)
    {
        $this->key = $key;
        $this->method = $method;
        $this->output = $output;
        $this->iv = $iv;
        $this->options = $options;
    }

    /**
     * 加密
     *
     * @param $str
     * @return string
     */
    public function encrypt($str)
    {
        $str = $this->pkcsPadding($str, 8);
        $sign = openssl_encrypt($str, $this->method, $this->key, $this->options, $this->iv);

        if ($this->output == self::OUTPUT_BASE64) {
            $sign = base64_encode($sign);
        } else if ($this->output == self::OUTPUT_HEX) {
            $sign = bin2hex($sign);
        }

        return $sign;
    }

    /**
     * 解密
     *
     * @param $encrypted
     * @return string
     */
    public function decrypt($encrypted)
    {
        if ($this->output == self::OUTPUT_BASE64) {
            $encrypted = base64_decode($encrypted);
        } else if ($this->output == self::OUTPUT_HEX) {
            $encrypted = hex2bin($encrypted);
        }

        $sign = @openssl_decrypt($encrypted, $this->method, $this->key, $this->options, $this->iv);
        $sign = $this->unPkcsPadding($sign);
        $sign = rtrim($sign);
        return $sign;
    }

    /**
     * 填充
     *
     * @param $str
     * @param $blocksize
     * @return string
     */
    private function pkcsPadding($str, $blocksize)
    {
        $pad = $blocksize - (strlen($str) % $blocksize);
        return $str . str_repeat(chr($pad), $pad);
    }

    /**
     * 去填充
     * 
     * @param $str
     * @return string
     */
    private function unPkcsPadding($str)
    {
        $pad = ord($str{strlen($str) - 1});
        if ($pad > strlen($str)) {
            return false;
        }
        return substr($str, 0, -1 * $pad);
    }

}


$key = 'key123456';
$iv = 'iv123456';

// DES CBC 加解密
$des = new DES($key, 'DES-CBC', DES::OUTPUT_BASE64, $iv);
echo $base64Sign = $des->encrypt('Hello DES CBC');
echo "\n";
echo $des->decrypt($base64Sign);
echo "\n";

// DES ECB 加解密
$des = new DES($key, 'DES-ECB', DES::OUTPUT_HEX);
echo $base64Sign = $des->encrypt('Hello DES ECB');
echo "\n";
echo $des->decrypt($base64Sign);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值