前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP官方现代化核心加密库 Sodium

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

作者头像
Tinywan
发布2024-08-19 16:09:25
1030
发布2024-08-19 16:09:25
举报
文章被收录于专栏:开源技术小栈

概述

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

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

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

安装

代码语言:javascript
复制
sudo pecl install -f libsodium

以下表示安装成功

代码语言:javascript
复制
Build process completed successfully
Installing '/usr/local/php-7.4/lib/php/extensions/no-debug-non-zts-20190902/sodium.so'
install ok: channel://pecl.php.net/libsodium-2.0.23
configuration option "php_ini" is not set to php.ini location
You should add "extension=sodium.so" to php.ini

安装错误

代码语言:javascript
复制
checking for libsodium... configure: error: Please install libsodium - See https://github.com/jedisct1/libsodium
ERROR: `/tmp/pear/temp/libsodium/configure --with-php-config=/usr/local/php-7.4/bin/php-config' failed

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

代码语言:javascript
复制
sudo apt install libsodium-dev

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

修改php.ini

代码语言:javascript
复制
extension=sodium.so

查看模块

代码语言:javascript
复制
php -m |grep sodium
sodium

简单使用

加密

代码语言:javascript
复制
<?php
/**
 * @desc libsodium
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/8/14 22:45
 */
require '../vendor/autoload.php';

$secretKey = sodium_crypto_secretbox_keygen();
$message = 'Sensitive 开源技术小栈';

$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$encryptedMessage = sodium_crypto_secretbox($message, $nonce, $secretKey);
var_dump($encryptedMessage);

输出

代码语言:javascript
复制
string(44) "�̥|}3�¢ҥ[zm񸗘 n؀\ƮV½¯C"

解密

代码语言:javascript
复制
$decryptedMessage = sodium_crypto_secretbox_open($encryptedMessage, $nonce, $secretKey);
var_dump($decryptedMessage);

输出

代码语言:javascript
复制
string(28) "Sensitive 开源技术小栈"

随机数据

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

随机字节

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

代码语言:javascript
复制
$string = \Sodium\randombytes_buf($num_bytes);

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

随机整数

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

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

代码语言:javascript
复制
$int = \Sodium\randombytes_uniform(100) + 1;

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

随机16位整数

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

代码语言:javascript
复制
$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 扩展实现
代码语言:javascript
复制
<?php
/**
 * @desc 对称加密算法 AES-256-GCM 代码示例
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/8/14 9:15
 */
declare(strict_types=1);

/**
 * @desc 加密
 * @param string $data
 * @param string $keygen
 * @param string $aad
 * @return array
 * @throws SodiumException
 * @throws \Random\RandomException
 * @author Tinywan(ShaoBo Wan)
 */
function aes256gcm_encrypt(string $data, string $keygen, string $aad = ''): array
{
    $iv = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES);
    $encrypt = sodium_crypto_aead_aes256gcm_encrypt($data, $aad, $iv, $keygen);
    return [
        'iv' => sodium_bin2base64($iv, SODIUM_BASE64_VARIANT_ORIGINAL),
        'aad' => sodium_bin2base64($aad, SODIUM_BASE64_VARIANT_ORIGINAL),
        'cipher_text' => sodium_bin2base64($encrypt, SODIUM_BASE64_VARIANT_ORIGINAL),
    ];
}

/**
 * @desc 解密
 * @param array $secretData
 * @param string $keygen
 * @return false|string
 * @throws SodiumException
 * @author Tinywan(ShaoBo Wan)
 */
function aes256gcm_decrypt(array $secretData, string $keygen)
{
    $iv = sodium_base642bin($secretData['iv'], SODIUM_BASE64_VARIANT_ORIGINAL);
    $aad = sodium_base642bin($secretData['aad'], SODIUM_BASE64_VARIANT_ORIGINAL);
    $cipherText = sodium_base642bin($secretData['cipher_text'], SODIUM_BASE64_VARIANT_ORIGINAL);
    return sodium_crypto_aead_aes256gcm_decrypt($cipherText, $aad, $iv, $keygen);
}

/**
 * @desc 主函数
 * @return void
 * @throws SodiumException
 * @throws \Random\RandomException
 * @author Tinywan(ShaoBo Wan)
 */
function main()
{
    if (!sodium_crypto_aead_aes256gcm_is_available()) {
        exit('Not support AES-256-GCM');
    }

    // 生成AES-256-GCM的密钥
    $keygen = sodium_crypto_aead_aes256gcm_keygen();
    // 加密原文内容
    $data = '开源技术小栈';
    $secretData = aes256gcm_encrypt($data, $keygen, 'tinywan');
    // 解密
    $plainText = aes256gcm_decrypt($secretData, $keygen);

    echo '[x] 加密原文:' .$data.PHP_EOL;
    echo '[x] 密文数据:' . json_encode($secretData) .PHP_EOL;
    echo '[x] 解密原文:' .$plainText.PHP_EOL;
}

main();

执行结果

代码语言:javascript
复制
[x] 加密原文:开源技术小栈
[x] 密文数据:{"iv":"KGMwg3PKlihp46qo","aad":"dGlueXdhbg==","cipher_text":"6oRb78FP33byDBhlBc0g6e7DYH0k3Oiethup+4YeWPN4Xw=="}
[x] 解密原文:开源技术小栈
Openssl扩展实现
代码语言:javascript
复制
<?php
/**
 * @desc openssl.php
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/8/14 9:22
 */
declare(strict_types=1);

/**
 * @desc 加密
 * @param string $data
 * @param string $keygen
 * @param string $aad
 * @return array
 * @throws \Random\RandomException
 * @author Tinywan(ShaoBo Wan)
 */
function aes256gcm_encrypt(string $data, string $keygen, string $aad = ''): array
{
    $cipher = 'aes-256-gcm';
    $ivLen = openssl_cipher_iv_length($cipher);
    $iv = random_bytes($ivLen);
    $encrypt = openssl_encrypt($data, $cipher, $keygen, OPENSSL_RAW_DATA, $iv, $tag, $aad);
    return [
        'iv' => base64_encode($iv),
        'tag' => base64_encode($tag),
        'aad' => base64_encode($aad),
        'cipher_text' => base64_encode($encrypt),
    ];
}

/**
 * @desc 解密
 * @param array $secretData
 * @param string $keygen
 * @return false|string
 * @author Tinywan(ShaoBo Wan)
 */
function aes256gcm_decrypt(array $secretData, string $keygen)
{
    $iv = base64_decode($secretData['iv']);
    $tag = base64_decode($secretData['tag']);
    $aad = base64_decode($secretData['aad']);
    $cipherText = base64_decode($secretData['cipher_text']);

    $cipher = 'aes-256-gcm';
    return openssl_decrypt($cipherText, $cipher, $keygen, OPENSSL_RAW_DATA, $iv, $tag, $aad);
}

/**
 * @desc 主函数
 * @return void
 * @throws SodiumException
 * @throws \Random\RandomException
 * @author Tinywan(ShaoBo Wan)
 */
function main()
{
    // AES-256-GCM需要32字节的密钥
    $keygen = bin2hex(random_bytes(16));
    // 加密
    $data = '开源技术小栈';
    $secretData = aes256gcm_encrypt($data, $keygen, 'tinywan');
    // 解密
    $plainText = aes256gcm_decrypt($secretData, $keygen);

    echo '[x] 加密原文:' . $data . PHP_EOL;
    echo '[x] 密文数据:' . json_encode($secretData) . PHP_EOL;
    echo '[x] 解密原文:' . $plainText . PHP_EOL;
}

main();

执行结果

代码语言:javascript
复制
[x] 加密原文:开源技术小栈
[x] 密文数据:{"iv":"CUit1bBOIOOM4Oaa","tag":"hlyP0MpykCNekBd1Kkobdw==","aad":"dGlueXdhbg==","cipher_text":"ToKc2+rVh8udQa\/bUtKodoC+"}
[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的作用就体现了出来。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 安装
  • 简单使用
  • 随机数据
    • 随机字节
      • 随机整数
        • 随机16位整数
        • 对称加密算法 AEAD_AES_256_GCM
          • Sodium 扩展实现
            • Openssl扩展实现
            • AAD参数(Additional Authenticated Data)
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档