跳至主要內容

php加密与解密

Moments大约 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);
上次编辑于:
贡献者: Moments