长亭百川云 - 文章详情

PHP官方现代化核心加密库 Sodium

Tinywan

30

2024-08-14

概述

Sodium crypto library是一个现代化的,易于使用的软件库,用于加密,解密,签名,密码散列等。

Sodium 出现的目的也是为了代替 Mcrypt 这个原来的加密扩展。在 PHP7.2 之后,Mcrypt 已经被移除,在 PHP7.1 时就已经被标记为过时。不过,Sodium 扩展的应用也并不是很多,大部分情况下我们都会使用 OpenSSL 来进行加密操作,同时,Sodium 扩展提供的函数也非常多

Sodium 扩展在 PHP7.2 后是跟随 PHP 源码一起发布的,只需要在编译的时候加上 --with-sodium 即可安装成功。如果是 PHP7.2 之前的版本,需要单独安装这个扩展。同时,操作系统中也需要安装 libsodium-devel 库.

安装

1sudo pecl install -f libsodium

以下表示安装成功

1Build process completed successfully
2Installing '/usr/local/php-7.4/lib/php/extensions/no-debug-non-zts-20190902/sodium.so'
3install ok: channel://pecl.php.net/libsodium-2.0.23
4configuration option "php_ini" is not set to php.ini location
5You should add "extension=sodium.so" to php.ini

安装错误

1checking for libsodium... configure: error: Please install libsodium - See https://github.com/jedisct1/libsodium
2ERROR: `/tmp/pear/temp/libsodium/configure --with-php-config=/usr/local/php-7.4/bin/php-config' failed

请安装扩展系统扩展库 libsodium-dev

1sudo apt install libsodium-dev
2
3Reading package lists... Done
4Building dependency tree       
5Reading state information... Done
6The following NEW packages will be installed:
7  libsodium-dev
80 upgraded, 1 newly installed, 0 to remove and 254 not upgraded.
9Need to get 160 kB of archives.
10After this operation, 760 kB of additional disk space will be used.
11Get:1 http://mirrors.cloud.aliyuncs.com/ubuntu bionic/main amd64 libsodium-dev amd64 1.0.16-2 [160 kB]
12Fetched 160 kB in 0s (1,514 kB/s) 
13Selecting previously unselected package libsodium-dev:amd64.
14(Reading database ... 161808 files and directories currently installed.)
15Preparing to unpack .../libsodium-dev_1.0.16-2_amd64.deb ...
16Unpacking libsodium-dev:amd64 (1.0.16-2) ...
17Setting up libsodium-dev:amd64 (1.0.16-2) ...

修改 php.ini

1extension=sodium.so

查看模块

1php -m |grep sodium
2sodium

简单使用

加密

1<?php
2/**
3 * @desc libsodium
4 * @author Tinywan(ShaoBo Wan)
5 * @date 2024/8/14 22:45
6 */
7require '../vendor/autoload.php';
8
9$secretKey = sodium_crypto_secretbox_keygen();
10$message = 'Sensitive 开源技术小栈';
11
12$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
13$encryptedMessage = sodium_crypto_secretbox($message, $nonce, $secretKey);
14var_dump($encryptedMessage);

输出

1string(44"�̥|}3�¢ҥ[zm񸗘 n؀\ƮV½¯C"

解密

1$decryptedMessage = sodium_crypto_secretbox_open($encryptedMessage, $nonce, $secretKey);
2var_dump($decryptedMessage);

输出

1string(28"Sensitive 开源技术小栈"

随机数据

在使用密码学时,经常需要随机字节或整数用于各种目的(加密密钥,随机数等)。具体来说,您需要使用密码学安全随机数生成器(CSPRNG).一个通用的随机数生成器是不够的。

随机字节

如果你需要一个由随机字节组成的字符串,你可以使用 \Sodium\randombytes_buf()

1$string = \Sodium\randombytes_buf($num_bytes);

如果将 $num_bytes 设置为32,则 $string 将是一个32字节的字符串,每个字节将是0到255之间的随机值的字符表示。

随机整数

如果你需要一个在0和一个特定上限之间的均匀分布的随机整数,你可以使用 \Sodium\randombytes_uniform()

例如,如果您需要一个介于1和100之间的数字:

1$int = \Sodium\randombytes_uniform(100) + 1;

请注意,在上面的例子中, $int 的可能值范围从1到100,因为 \Sodium\randombytes_uniform 将返回0到99之间的随机整数。100 包括在 \Sodium\randombytes_uniform(100) 的可能输出值中。

随机16位整数

返回一个介于0和65535(含)之间的整数,服从均匀分布。

1$tcp_port = \Sodium\randombytes_random16();

对称加密算法 AEAD_AES_256_GCM

对数据进行 AES-256-GCM 加密和解密。可以使用 opensslsodium 扩展来实现加密,它们都支持 AES-256-GCM 算法,下面将给出两种扩展的代码示例。

微信支付最新的 V3 版本接口,微信返回的报文中,如果涉及敏感信息,是需要基于 AEAD_AES_256_GCM 进行解密的。在官方文档中,也提供了 PHP 对应的解密方式,其中使用的就是 Sodium 扩展库中的函数。

如何解密证书和回调报文: https://pay.weixin.qq.com/docs/merchant/development/interface-rules/certificate-callback-decryption.html#_2-%E8%A7%A3%E5%AF%86

Sodium 扩展实现

1<?php
2/**
3 * @desc 对称加密算法 AES-256-GCM 代码示例
4 * @author Tinywan(ShaoBo Wan)
5 * @date 2024/8/14 9:15
6 */
7declare(strict_types=1);
8
9/**
10 * @desc 加密
11 * @param string $data
12 * @param string $keygen
13 * @param string $aad
14 * @return array
15 * @throws SodiumException
16 * @throws \Random\RandomException
17 * @author Tinywan(ShaoBo Wan)
18 */
19function aes256gcm_encrypt(string $datastring $keygenstring $aad ''): array
20{
21    $iv = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES);
22    $encrypt = sodium_crypto_aead_aes256gcm_encrypt($data$aad$iv$keygen);
23    return [
24        'iv' => sodium_bin2base64($iv, SODIUM_BASE64_VARIANT_ORIGINAL),
25        'aad' => sodium_bin2base64($aad, SODIUM_BASE64_VARIANT_ORIGINAL),
26        'cipher_text' => sodium_bin2base64($encrypt, SODIUM_BASE64_VARIANT_ORIGINAL),
27    ];
28}
29
30/**
31 * @desc 解密
32 * @param array $secretData
33 * @param string $keygen
34 * @return false|string
35 * @throws SodiumException
36 * @author Tinywan(ShaoBo Wan)
37 */
38function aes256gcm_decrypt(array $secretDatastring $keygen)
39{
40    $iv = sodium_base642bin($secretData['iv'], SODIUM_BASE64_VARIANT_ORIGINAL);
41    $aad = sodium_base642bin($secretData['aad'], SODIUM_BASE64_VARIANT_ORIGINAL);
42    $cipherText = sodium_base642bin($secretData['cipher_text'], SODIUM_BASE64_VARIANT_ORIGINAL);
43    return sodium_crypto_aead_aes256gcm_decrypt($cipherText$aad$iv$keygen);
44}
45
46/**
47 * @desc 主函数
48 * @return void
49 * @throws SodiumException
50 * @throws \Random\RandomException
51 * @author Tinywan(ShaoBo Wan)
52 */
53function main()
54{
55    if (!sodium_crypto_aead_aes256gcm_is_available()) {
56        exit('Not support AES-256-GCM');
57    }
58
59    // 生成AES-256-GCM的密钥
60    $keygen = sodium_crypto_aead_aes256gcm_keygen();
61    // 加密原文内容
62    $data '开源技术小栈';
63    $secretData = aes256gcm_encrypt($data$keygen'tinywan');
64    // 解密
65    $plainText = aes256gcm_decrypt($secretData$keygen);
66
67    echo '[x] 加密原文:' .$data.PHP_EOL;
68    echo '[x] 密文数据:' . json_encode($secretData) .PHP_EOL;
69    echo '[x] 解密原文:' .$plainText.PHP_EOL;
70}
71
72main();

执行结果

1[x] 加密原文:开源技术小栈
2[x] 密文数据:{"iv":"KGMwg3PKlihp46qo","aad":"dGlueXdhbg==","cipher_text":"6oRb78FP33byDBhlBc0g6e7DYH0k3Oiethup+4YeWPN4Xw=="}
3[x] 解密原文:开源技术小栈

Openssl扩展实现

1<?php
2/**
3 * @desc openssl.php
4 * @author Tinywan(ShaoBo Wan)
5 * @date 2024/8/14 9:22
6 */
7declare(strict_types=1);
8
9/**
10 * @desc 加密
11 * @param string $data
12 * @param string $keygen
13 * @param string $aad
14 * @return array
15 * @throws \Random\RandomException
16 * @author Tinywan(ShaoBo Wan)
17 */
18function aes256gcm_encrypt(string $datastring $keygenstring $aad ''): array
19{
20    $cipher 'aes-256-gcm';
21    $ivLen = openssl_cipher_iv_length($cipher);
22    $iv = random_bytes($ivLen);
23    $encrypt = openssl_encrypt($data$cipher$keygen, OPENSSL_RAW_DATA, $iv$tag$aad);
24    return [
25        'iv' => base64_encode($iv),
26        'tag' => base64_encode($tag),
27        'aad' => base64_encode($aad),
28        'cipher_text' => base64_encode($encrypt),
29    ];
30}
31
32/**
33 * @desc 解密
34 * @param array $secretData
35 * @param string $keygen
36 * @return false|string
37 * @author Tinywan(ShaoBo Wan)
38 */
39function aes256gcm_decrypt(array $secretDatastring $keygen)
40{
41    $iv = base64_decode($secretData['iv']);
42    $tag = base64_decode($secretData['tag']);
43    $aad = base64_decode($secretData['aad']);
44    $cipherText = base64_decode($secretData['cipher_text']);
45
46    $cipher 'aes-256-gcm';
47    return openssl_decrypt($cipherText$cipher$keygen, OPENSSL_RAW_DATA, $iv$tag$aad);
48}
49
50/**
51 * @desc 主函数
52 * @return void
53 * @throws SodiumException
54 * @throws \Random\RandomException
55 * @author Tinywan(ShaoBo Wan)
56 */
57function main()
58{
59    // AES-256-GCM需要32字节的密钥
60    $keygen = bin2hex(random_bytes(16));
61    // 加密
62    $data '开源技术小栈';
63    $secretData = aes256gcm_encrypt($data$keygen'tinywan');
64    // 解密
65    $plainText = aes256gcm_decrypt($secretData$keygen);
66
67    echo '[x] 加密原文:' . $data . PHP_EOL;
68    echo '[x] 密文数据:' . json_encode($secretData) . PHP_EOL;
69    echo '[x] 解密原文:' . $plainText . PHP_EOL;
70}
71
72main();

执行结果

1[x] 加密原文:开源技术小栈
2[x] 密文数据:{"iv":"CUit1bBOIOOM4Oaa","tag":"hlyP0MpykCNekBd1Kkobdw==","aad":"dGlueXdhbg==","cipher_text":"ToKc2+rVh8udQa\/bUtKodoC+"}
3[x] 解密原文:开源技术小栈

AAD参数(Additional Authenticated Data)

在上面的代码示例中,可以看到在加密的时候有一个 $aad 参数,如果在加密的时候使用了这个参数,那么在解密时也需要使用同样的AAD值才能成功解密。

什么时候会需要用到AAD呢?

用户 Tinywan 在微信公众号上写了一篇私密文章,微信公众号使用 AES-256-GCM 加密了这篇文章,然后存储到了数据库里, AAD 的取值是 Tinywan 的用户 ID

后续如果 Tinywan 去查看这篇文章,微信公众号会使用 Tinywan 的用户 ID(AAD值) 去解密,因为解密的 AAD 值与加密的 AAD 值相同,所以可以成功解密。

然后有一个黑客 ShaoBoWan ,他也向微信公众号发出解密这篇文章的请求,微信公众号就会使用 ShaoBoWan 的用户 ID 作为 AAD 去解密这篇文章,但因为 AAD 值错误,肯定是解密不了的,所以这时候 AAD 的作用就体现了出来。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2