php加密与解密
大约 6 分钟
php加密与解密
单向加密
md5,sha256,crypt
因为是单向加密,所以没有解密方法,同样的数据生成的值是一样的.
彩虹表,现如今的网络用户信息很容易被泄露,常用的加密值量并不大.
应对的办法可以加点盐,咸淡随心.
对称加密
采用单密钥的加密方法,同一个密钥同时用作加密和解密,亦称单密钥加密.
在实际应用中,可以理解为一对一加密.
对称加密优缺点
- 算法公开
- 计算量小
- 速度快,效率高
- 双方都有密钥,容易泄密
- 密钥在传输的过程中不安全
- 密钥管理比较麻烦
常用对称加密算法
- DES,Data Encryption Standard,数据加密标准
- 3DES,DES升级版,对每个数据块应用三次DES加密算法
- AES,Advanced Encryption Standard,高级加密标准,用于取代DES
- CAMELLIA,分组密码算法,安全和效率与AES差不多
加密模式
- ECB,Electronic Codebook,电码本
- CBC,Cipher Block Chaining,密文分组链接模式
- CTR,Counter,计算器模式
- OFB,Cipher FeedBack,密码反馈模式
- CFB,Output FeedBack,输出反馈模式
CBC加密模式
CBC (Cipher Block Chaining, 密码分组链接) 模式中每一个分组要先和前一个分组加密后的数据进行XOR异或操作,然后再进行加密。
优点:
串行运算,相同明文不同密文. 缺点:
需要初始向量(IV),不能并行处理.
名词 | 解释 |
---|---|
Plaintext | 明文,待加密的数据. |
IV | 初始向量,用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文. |
Key | 对称密钥 |
Ciphertext | 密文数据 |
分组 | 以固定位数做为一个块 |
关于IV的理解
DES中使用了8字节的IV,AES中使用了16字节的IV,为什么要指定大小?
个人理解为:
在分组的加密算法中,固定块的大小就决定了IV的大小,和内存对齐差不多,主要是为了提高效率.
DES中有使用8字节为一块,AES中有使用16字节为一块,从而出现差异.
从参数上讲,固定让用户传入指定大小,是为了提高安全性,否则溢出则被截取,不足则默认填充.
分组越大,理论上讲安全性会高一些,但消耗会大一些.
1.明文分组对齐(如16字节分组对齐),位数不足则填充特殊字符.
2.初始化对称密钥,初始向量IV(如16字节)
3.初始向量IV异或第一组明文得到临时密文,再用对称密钥对临时密文加密生成迭代向量(也是第一组的最终密文).
4.迭代向量异或第二组明文得到临时密文,再用对称密钥对临时密文加密生成迭代向量(也是第二组的最终密文).
5.一直迭代到最后一组明文.
6.将初始向量和迭代向量(各组的最终密文)拼接在一起,得到最终的密文.
使用OpenSSL库
$data = 'Moments'; // 明文
$cipherAlgo = 'AES-256-CBC'; // 加密算法和加密模式
$passphrase = '12345678'; // 对称密钥
$options = 0; // 自动填充,返回base64
$iv = str_repeat(1, 16); // 初始向量
openssl_encrypt($data, $cipherAlgo, $passphrase, $options, $iv);
调用openssl_get_cipher_methods
加密算法
0 => "AES-128-CBC"
1 => "AES-128-CBC-HMAC-SHA1"
2 => "AES-128-CFB"
3 => "AES-128-CFB1"
4 => "AES-128-CFB8"
5 => "AES-128-CTR"
6 => "AES-128-ECB"
7 => "AES-128-OFB"
8 => "AES-128-XTS"
9 => "AES-192-CBC"
10 => "AES-192-CFB"
11 => "AES-192-CFB1"
12 => "AES-192-CFB8"
13 => "AES-192-CTR"
14 => "AES-192-ECB"
15 => "AES-192-OFB"
16 => "AES-256-CBC"
17 => "AES-256-CBC-HMAC-SHA1"
18 => "AES-256-CFB"
19 => "AES-256-CFB1"
20 => "AES-256-CFB8"
21 => "AES-256-CTR"
22 => "AES-256-ECB"
23 => "AES-256-OFB"
24 => "AES-256-XTS"
25 => "BF-CBC"
26 => "BF-CFB"
27 => "BF-ECB"
28 => "BF-OFB"
29 => "CAMELLIA-128-CBC"
30 => "CAMELLIA-128-CFB"
31 => "CAMELLIA-128-CFB1"
32 => "CAMELLIA-128-CFB8"
33 => "CAMELLIA-128-ECB"
34 => "CAMELLIA-128-OFB"
35 => "CAMELLIA-192-CBC"
36 => "CAMELLIA-192-CFB"
37 => "CAMELLIA-192-CFB1"
38 => "CAMELLIA-192-CFB8"
39 => "CAMELLIA-192-ECB"
40 => "CAMELLIA-192-OFB"
41 => "CAMELLIA-256-CBC"
42 => "CAMELLIA-256-CFB"
43 => "CAMELLIA-256-CFB1"
44 => "CAMELLIA-256-CFB8"
45 => "CAMELLIA-256-ECB"
46 => "CAMELLIA-256-OFB"
47 => "CAST5-CBC"
48 => "CAST5-CFB"
49 => "CAST5-ECB"
50 => "CAST5-OFB"
51 => "ChaCha"
52 => "DES-CBC"
53 => "DES-CFB"
54 => "DES-CFB1"
55 => "DES-CFB8"
56 => "DES-ECB"
57 => "DES-EDE"
58 => "DES-EDE-CBC"
59 => "DES-EDE-CFB"
60 => "DES-EDE-OFB"
61 => "DES-EDE3"
62 => "DES-EDE3-CBC"
63 => "DES-EDE3-CFB"
64 => "DES-EDE3-CFB1"
65 => "DES-EDE3-CFB8"
66 => "DES-EDE3-OFB"
67 => "DES-OFB"
68 => "DESX-CBC"
69 => "GOST 28147-89"
70 => "RC2-40-CBC"
71 => "RC2-64-CBC"
72 => "RC2-CBC"
73 => "RC2-CFB"
74 => "RC2-ECB"
75 => "RC2-OFB"
76 => "RC4"
77 => "RC4-40"
78 => "RC4-HMAC-MD5"
laravel框架中的应用
框架中封装了一个全局方法encrypt
.
只留主逻辑,其他的精简掉,源码在Illuminate\Encryption\Encrypter类中.
在Encrypter的构造函数中,要传入$cipher和$key,
$key使用的是APP_KEY去掉`base64:`前缀的字符然后base64解码.
$cipher使用的是config('app.cipher');
// $data = 'Moments'; // 明文
// dump(encrypt($data, true));
$data = 'Moments';
$key = base64_decode(Str::after(config('app.key'), 'base64:'));
$cipher = config('app.cipher', 'AES-256-CBC')
$iv = random_bytes(openssl_cipher_iv_length($cipher)); // 初始向量
$value = openssl_encrypt(serialize($data), $cipher, $key, 0, $iv); // 加密
$iv = base64_encode($iv);
$mac = hash_hmac('sha256', $iv.$value, $key); // 加签
$json = json_encode(compact('iv', 'value', 'mac'), JSON_UNESCAPED_SLASHES); // 最终结果
$result = base64_encode($json);
非对称加密
由公钥和私钥组成,用公钥加密需用私钥解密,用私钥加密需用公钥解密.
公钥可以提供给多用户,如果说对称加密是一对一的关系,那么非对称加密就是一对多的关系.
生成非对称加密的公钥和私钥
openssl genrsa -out rsa_private.pem 384 // 生成标准私钥,最小值384,如果生成的密钥太短会出现null的加密结果
openssl rsa -AES-256-CBC -in rsa_private.pem -pubout -out public.pem // 生成公钥
openssl pkcs8 -topk8 -inform PEM -in rsa_private.pem -outform PEM -nocrypt -out private.pem // 将原始的RSA私钥转换为pkcs8模式
非对称加密实例
// 用公钥加密需用私钥解密
\openssl_public_encrypt($plaintext, $ciphertext, $this->publicKey);
\openssl_private_decrypt($ciphertext, $plaintext, $this->privateKey);
// 用私钥加密需用公钥解密
\openssl_private_encrypt($plaintext, $ciphertext, $this->privateKey);
\openssl_public_decrypt($ciphertext, $plaintext, $this->publicKey);
/*
* 使用资源id
*/
$privateId = \openssl_pkey_get_private($this->privateKey);
$publicId = \openssl_pkey_get_public($this->publicKey);
// 用公钥加密需用私钥解密
\openssl_public_encrypt($plaintext, $ciphertext, $publicId);
\openssl_private_decrypt($ciphertext, $plaintext, $privateId);
// 用私钥加密需用公钥解密
\openssl_private_encrypt($plaintext, $ciphertext, $privateId);
\openssl_public_decrypt($ciphertext, $plaintext, $publicId);
/*
* 验证签名
*/
\openssl_sign($plaintext, $signature, $privateId);
$state = \openssl_verify($plaintext, $signature, $publicId);
if ($state == 1) {
echo "验证签名成功\n";
}
/*
* 生成新的密钥对
*/
$private_key_id = \openssl_pkey_new(['private_key_bits' => 384]);
\openssl_pkey_export($private_key_id, $private_key);
$public_key = \openssl_pkey_get_details($private_key_id)['key'];
\openssl_private_encrypt($plaintext, $ciphertext, $private_key);
\openssl_public_decrypt($ciphertext, $plaintext, $public_key);
// 释放资源
\openssl_free_key($publicId);
\openssl_free_key($privateId);