用PHP钠在双向数据流机器上进行字符串加密和解密的指南

107 阅读1分钟

如果你想对一个字符串进行加密和解密**(只有当数据离开当前机器,并且发送方和接收方机器被允许解密来自对方的数据**时),你可以使用下面的例子。加密的数据将永远是动态的,所以对于给定的字符串,其结果将总是不同的。它在PHP7.2以上的机器上使用Sodium。**注意:**阅读代码中的注释。

类别

你可能需要在你的composer.json 文件中添加"ext-sodium": "*" 。也请阅读这里的相关函数的具体作用。

/**
 * You have to use same "keys" and "nonce" to encrypt plain data and decrypt encrypted data.
 * Use if the sender and receiver machines are allowed to decrypt the data coming from each other. A <-> B
 *
 * Use bin2hex() on encrypted data before sending.
 * Use hex2bin() on encrypted data before decrypting.
 */
class Both
{
    /**
     * This is what sender computer A does.
     *
     * @param string $plainData This is what sender computer A will send to receiver computer B
     * @param string $nonce
     * @param string $aSecretKey This belongs to sender computer A where the message will be sent from
     * @param string $bComputersPublicKey This belongs to receiver computer B where the message will be sent to
     *
     * @return string
     */
    public function encryptA(string $plainData, string $nonce, string $aSecretKey, string $bComputersPublicKey): string
    {
        return $nonce.sodium_crypto_box($plainData, $nonce, $aSecretKey.$bComputersPublicKey);
    }

    /**
     * This is what receiver computer B does.
     *
     * @param string $encryptedData This comes from the sender computer A
     * @param string $nonce
     * @param string $bSecretKey This belongs to receiver computer B where the message will be handled
     * @param string $aComputersPublicKey This belongs to sender computer A where the message comes from
     *
     * @return string
     */
    public function decryptB(string $encryptedData, string $nonce, string $bSecretKey, string $aComputersPublicKey): string
    {
        return sodium_crypto_box_open($encryptedData, $nonce, $bSecretKey.$aComputersPublicKey);
    }

    /**
     * This is what sender computer B does.
     *
     * @param string $plainData This is what sender computer B will send to receiver computer A
     * @param string $nonce
     * @param string $bSecretKey This belongs to sender computer B where the message will be sent from
     * @param string $aComputersPublicKey This belongs to receiver computer A where the message will be sent to
     *
     * @return string
     */
    public function encryptB(string $plainData, string $nonce, string $bSecretKey, string $aComputersPublicKey): string
    {
        return $nonce.sodium_crypto_box($plainData, $nonce, $bSecretKey.$aComputersPublicKey);
    }

    /**
     * This is what receiver computer A does.
     *
     * @param string $encryptedData This comes from the sender computer B
     * @param string $nonce
     * @param string $aSecretKey This belongs to receiver computer A where the message will be handled
     * @param string $bComputersPublicKey This belongs to sender computer B where the message comes from
     *
     * @return string
     */
    public function decryptA(string $encryptedData, string $nonce, string $aSecretKey, string $bComputersPublicKey): string
    {
        return sodium_crypto_box_open($encryptedData, $nonce, $aSecretKey.$bComputersPublicKey);
    }
}

测试

class BothTest extends TestCase
{
    private $aComputersKeyPair;
    private $aComputersSecretKey;
    private $aComputersPublicKey;
    private $bComputersKeyPair;
    private $bComputersSecretKey;
    private $bComputersPublicKey;

    protected function setUp()
    {
        $this->aComputersKeyPair = sodium_crypto_box_keypair();
        $this->aComputersSecretKey = sodium_crypto_box_secretkey($this->aComputersKeyPair);
        $this->aComputersPublicKey = sodium_crypto_box_publickey($this->aComputersKeyPair);
        $this->bComputersKeyPair = sodium_crypto_box_keypair();
        $this->bComputersSecretKey = sodium_crypto_box_secretkey($this->bComputersKeyPair);
        $this->bComputersPublicKey = sodium_crypto_box_publickey($this->bComputersKeyPair);
    }

    public function testEncryptA(): void
    {
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

        $dataToBeSent = (new Both())
            ->encryptA('inanzzz', $nonce, $this->aComputersSecretKey, $this->bComputersPublicKey);

        $this->assertIsString($dataToBeSent);
    }

    public function testDecryptB(): void
    {
        // Sender
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

        $dataReceived = (new Both())
            ->encryptA('inanzzz', $nonce, $this->aComputersSecretKey, $this->bComputersPublicKey);

        // Receiver
        $nonce = mb_substr($dataReceived, 0, 24, '8bit');
        $dataReceived = mb_substr($dataReceived, 24, null, '8bit');

        $result = (new Both())
            ->decryptB($dataReceived, $nonce, $this->bComputersSecretKey, $this->aComputersPublicKey);

        $this->assertSame('inanzzz', $result);
    }

    public function testEncryptB(): void
    {
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

        $dataToBeSent = (new Both())
            ->encryptB('inanzzz', $nonce, $this->bComputersSecretKey, $this->aComputersPublicKey);

        $this->assertIsString($dataToBeSent);
    }

    public function testDecryptA(): void
    {
        // Sender
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

        $dataReceived = (new Both())
            ->encryptB('inanzzz', $nonce, $this->bComputersSecretKey, $this->aComputersPublicKey);

        // Receiver
        $nonce = mb_substr($dataReceived, 0, 24, '8bit');
        $dataReceived = mb_substr($dataReceived, 24, null, '8bit');

        $result = (new Both())
            ->decryptA($dataReceived, $nonce, $this->aComputersSecretKey, $this->bComputersPublicKey);

        $this->assertSame('inanzzz', $result);
    }
}