【PHP】使用sodium扩展进行非对称加解密

一、前言

在PHP中一般有三个密码库(扩展):mcrypt、openssl、sodium

mcrypt 扩展从 PHP 7.1 开始被标记为过时,并从 PHP 7.2 开始被正式移除。

openssl是目前使用最广泛的扩展,提供的函数支持多种多样的自定义参数,但是如果参数使用不当,可能会造成安全问题,对开发者的密码学知识要求较高。

而 sodium 是一个更现代化的扩展,自从 PHP 7.2 开始就被引入到了PHP核心库中,无需安装就可以直接使用。sodium 提供的函数可自定义参数较少(因为它已经帮开发者选择好了最佳的参数配置),使用更加方便,安全性也足够高,对开发者的密码学知识要求较低,但是这个扩展目前使用人数还比较少,网上资料也相对较少。

下面介绍如何使用 sodium 扩展进行非对称加解密,非对称加密算法常见的有 RSAECC 两种,sodium扩展使用的是 ECC 算法(详见这里),因此如果你需要的是RSA算法,那么就不适合使用 sodium 。

RSA 与 ECC 算法的对比

在这里插入图片描述

除了非对称加解密,sodium也支持对称加解密算法,例如AES-256-GCM

非对称加密又分为 “有身份验证” 和 “无身份验证” 两种。身份验证的意思是,通讯双方除了加解密消息外,还要验证消息发送者的真实身份(使用私钥签名的方式),这种场景下,消息发送方 和 消息接收方 都需要各自有一对公私钥;

而无身份验证的场景下,只需消息接收方有一对公私钥即可,然后通过某种安全的方式将公钥分发给发送方,发送方使用公钥对消息进行加密,而接收方可以使用私钥解密消息,但无法识别或验证发送方的身份。

分发公钥最简单、最常见的方法是通过HTTPS连接。例如,发送者可以通过HTTPS从接收者的网站下载接收者的公钥。

由于不存在身份验证机制或nonce值,会容易受到重放攻击。

二、无身份验证的非对称加解密

创建一个 Alice 类,然后模拟给 Alice 发消息:

class Alice
{
    private $keypair;

    public function __construct()
    {
        // 生成公私钥
        $this->keypair = sodium_crypto_box_keypair();
    }

    /**
     * 获取Alice的私钥
     *
     * @return string
     */
    private function getPrivateKey(): string
    {
        return sodium_crypto_box_secretkey($this->keypair);
    }

    /**
     * 获取Alice的公钥
     *
     * @return string
     */
    public function getPublicKey(): string
    {
        return sodium_crypto_box_publickey($this->keypair);
    }

    /**
     * 解密消息
     *
     * @param string $secretText 密文
     *
     * @return false|string 解密后的明文
     */
    public function receiveMessage(string $secretText)
    {
        return sodium_crypto_box_seal_open($secretText, $this->keypair);
    }
}

function main()
{
    // 模拟给Alice发送消息
    $Alice = new Alice();
    $secretText = sodium_crypto_box_seal("Hello Alice, you don't know who I am", $Alice->getPublicKey());
    // Alice解密消息
    $plainText = $Alice->receiveMessage($secretText);
    echo "Alice接收到了消息:$plainText\n";
}

main();

三、有身份验证的非对称加解密

创建 Alice 和 Bob 两个类,模拟 Alice 和 Bob 之间的通讯。

Alice类

class Alice
{
    private $keypair;

    public function __construct()
    {
        // 生成公私钥
        $this->keypair = sodium_crypto_box_keypair();
    }

    /**
     * 获取Alice的私钥
     *
     * @return string
     */
    private function getPrivateKey(): string
    {
        return sodium_crypto_box_secretkey($this->keypair);
    }

    /**
     * 获取Alice的公钥
     *
     * @return string
     */
    public function getPublicKey(): string
    {
        return sodium_crypto_box_publickey($this->keypair);
    }

    /**
     * 以Alice的身份发送消息
     *
     * @param string $message         明文消息
     * @param string $recipientPubKey 接收者的公钥,用于加密消息
     *
     * @return array 返回nonce值和密文
     */
    public function sendMessageTo(string $message, string $recipientPubKey): array
    {
        $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($this->getPrivateKey(), $recipientPubKey);
        $nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);
        $secretText = sodium_crypto_box($message, $nonce, $keypair);
        return [
            'nonce' => $nonce,
            'secret_text' => $secretText,
        ];
    }

    /**
     * 以Alice的身份接收消息
     *
     * @param string $secretText   密文
     * @param string $nonce        nonce值
     * @param string $senderPubKey 消息发送者的公钥,用于验证发送者的身份
     *
     * @return false|string 解密后的明文
     */
    public function receiveMessageFrom(string $secretText, string $nonce, string $senderPubKey)
    {
        $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($this->getPrivateKey(), $senderPubKey);
        return sodium_crypto_box_open($secretText, $nonce, $keypair);
    }
}

Bob类

class Bob
{
    private $keypair;

    public function __construct()
    {
        // 生成公私钥
        $this->keypair = sodium_crypto_box_keypair();
    }

    /**
     * 获取Bob的私钥
     *
     * @return string
     */
    private function getPrivateKey(): string
    {
        return sodium_crypto_box_secretkey($this->keypair);
    }

    /**
     * 获取Bob的公钥
     *
     * @return string
     */
    public function getPublicKey(): string
    {
        return sodium_crypto_box_publickey($this->keypair);
    }

    /**
     * 以Bob的身份发送消息
     *
     * @param string $message         明文消息
     * @param string $recipientPubKey 接收者的公钥,用于加密消息
     *
     * @return array 返回nonce值和密文
     */
    public function sendMessageTo(string $message, string $recipientPubKey): array
    {
        $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($this->getPrivateKey(), $recipientPubKey);
        $nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);
        $secretText = sodium_crypto_box($message, $nonce, $keypair);
        return [
            'nonce' => $nonce,
            'secret_text' => $secretText,
        ];
    }

    /**
     * 以Bob的身份接收消息
     *
     * @param string $secretText   密文
     * @param string $nonce        nonce值
     * @param string $senderPubKey 消息发送者的公钥,用于验证发送者的身份
     *
     * @return false|string 解密后的明文
     */
    public function receiveMessageFrom(string $secretText, string $nonce, string $senderPubKey)
    {
        $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($this->getPrivateKey(), $senderPubKey);
        return sodium_crypto_box_open($secretText, $nonce, $keypair);
    }
}

模拟 Alice 和 Bob 通讯

function main()
{
    $Alice = new Alice();
    $Bob = new Bob();

    /* 以Alice的身份给Bob发送消息 */
    $msg = $Alice->sendMessageTo('Hello Bob, I am Alice', $Bob->getPublicKey());
    // Bob解密消息
    $plainText = $Bob->receiveMessageFrom($msg['secret_text'], $msg['nonce'], $Alice->getPublicKey());
    echo "Bob接收到了Alice的消息:$plainText\n";

    /* 以Bob的身份给Alice发送消息 */
    $msg = $Bob->sendMessageTo('Hello Alice, I am Bob', $Alice->getPublicKey());
    // Alice解密消息
    $plainText = $Alice->receiveMessageFrom($msg['secret_text'], $msg['nonce'], $Bob->getPublicKey());
    echo "Alice接收到了Bob的消息:$plainText\n";

}

main();

四、参阅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值