使用 PHP 的最简单的双向加密

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9262109/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 06:27:06  来源:igfitidea点击:

Simplest two-way encryption using PHP

phpsecurityencryptioncryptographyencryption-symmetric

提问by user1206970

What is the simplest way of doing two way encryption in common PHP installs?

在常见的 PHP 安装中进行双向加密的最简单方法是什么?

I need to be able to encrypt data with a string key, and use the same key to decrypt on the other end.

我需要能够使用字符串密钥加密数据,并使用相同的密钥在另一端解密。

The security isn't as big of a concern as the portability of the code, so I'd like to be able to keep things as simple as possible. Currently, I am using an RC4 implementation, but if I can find something natively supported I figure I can save a lot of unnecessary code.

安全性不像代码的可移植性那么重要,所以我希望能够使事情尽可能简单。目前,我正在使用 RC4 实现,但如果我能找到本机支持的东西,我想我可以节省很多不必要的代码。

采纳答案by 472084

Edited:

编辑:

You should really be using openssl_encrypt()& openssl_decrypt()

你真的应该使用openssl_encrypt()& openssl_decrypt()

As Scottsays, Mcrypt is not a good idea as it has not been updated since 2007.

正如Scott所说,Mcrypt 不是一个好主意,因为它自 2007 年以来就没有更新过。

There is even an RFC to remove Mcrypt from PHP - https://wiki.php.net/rfc/mcrypt-viking-funeral

甚至有一个 RFC 可以从 PHP 中删除 Mcrypt - https://wiki.php.net/rfc/mcrypt-viking-funeral

回答by Scott Arciszewski

Important: Unless you have a veryparticular use-case, do not encrypt passwords, use a password hashing algorithm instead. When someone says they encrypttheir passwords in a server-side application, they're either uninformed or they're describing a dangerous system design. Safely storing passwordsis a totally separate problem from encryption.

重要提示:除非您有非常特殊的用例,否则不要加密密码,而是使用密码散列算法。当有人说他们在服务器端应用程序中加密了他们的密码时,他们要么是不知情,要么是在描述危险的系统设计。安全存储密码是一个与加密完全不同的问题。

Be informed. Design safe systems.

被告知。设计安全系统。

Portable Data Encryption in PHP

PHP 中的便携式数据加密

If you're using PHP 5.4 or newerand don't want to write a cryptography module yourself, I recommend using an existing library that provides authenticated encryption. The library I linked relies only on what PHP provides and is under periodic review by a handful of security researchers. (Myself included.)

如果您使用PHP 5.4 或更新版本并且不想自己编写加密模块,我建议您使用提供经过身份验证的加密的现有库。我链接的库仅依赖于 PHP 提供的内容,并由少数安全研究人员定期。(包括我自己。)

If your portability goals do not prevent requiring PECL extensions, libsodiumis highlyrecommended over anything you or I can write in PHP.

如果您的可移植性目标不妨碍需要 PECL 扩展,则强烈建议您使用libsodium,而不是您或我可以用 PHP 编写的任何内容。

Update (2016-06-12):You can now use sodium_compatand use the same crypto libsodium offers without installing PECL extensions.

更新(2016-06-12):您现在可以使用sodium_compat并使用相同的crypto libsodium 产品,而无需安装PECL 扩展。

If you want to try your hand at cryptography engineering, read on.

如果您想尝试密码学工程,请继续阅读。



First, you should take the time to learn the dangers of unauthenticated encryptionand the Cryptographic Doom Principle.

首先,您应该花时间了解未经身份验证的加密的危险密码学末日原则

  • Encrypted data can still be tampered with by a malicious user.
  • Authenticating the encrypted data prevents tampering.
  • Authenticating the unencrypted data does not prevent tampering.
  • 加密数据仍然可以被恶意用户篡改。
  • 对加密数据进行身份验证可防止篡改。
  • 验证未加密的数据并不能防止篡改。

Encryption and Decryption

加密和解密

Encryption in PHP is actually simple (we're going to use openssl_encrypt()and openssl_decrypt()once you have made some decisions about how to encrypt your information. Consult openssl_get_cipher_methods()for a list of the methods supported on your system. The best choice is AES in CTR mode:

PHP 中的加密实际上很简单(我们将要使用openssl_encrypt()openssl_decrypt()一旦您决定如何加密您的信息。请咨询openssl_get_cipher_methods()您的系统支持的方法列表。最好的选择是CTR 模式下的 AES

  • aes-128-ctr
  • aes-192-ctr
  • aes-256-ctr
  • aes-128-ctr
  • aes-192-ctr
  • aes-256-ctr

There is currently no reason to believe that the AES key sizeis a significant issue to worry about (bigger is probably notbetter, due to bad key-scheduling in the 256-bit mode).

目前没有理由相信AES 密钥大小是一个值得担心的重要问题(由于 256 位模式下的密钥调度不好,可能不是越大越好)。

Note: We are not using mcryptbecause it is abandonwareand has unpatched bugsthat might be security-affecting. Because of these reasons, I encourage other PHP developers to avoid it as well.

注意:我们没有使用mcrypt它,因为它是废弃软件并且有可能影响安全的未修补错误。由于这些原因,我鼓励其他 PHP 开发人员也避免使用它。

Simple Encryption/Decryption Wrapper using OpenSSL

使用 OpenSSL 的简单加密/解密包装器

class UnsafeCrypto
{
    const METHOD = 'aes-256-ctr';

    /**
     * Encrypts (but does not authenticate) a message
     * 
     * @param string $message - plaintext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encode - set to TRUE to return a base64-encoded 
     * @return string (raw binary)
     */
    public static function encrypt($message, $key, $encode = false)
    {
        $nonceSize = openssl_cipher_iv_length(self::METHOD);
        $nonce = openssl_random_pseudo_bytes($nonceSize);

        $ciphertext = openssl_encrypt(
            $message,
            self::METHOD,
            $key,
            OPENSSL_RAW_DATA,
            $nonce
        );

        // Now let's pack the IV and the ciphertext together
        // Naively, we can just concatenate
        if ($encode) {
            return base64_encode($nonce.$ciphertext);
        }
        return $nonce.$ciphertext;
    }

    /**
     * Decrypts (but does not verify) a message
     * 
     * @param string $message - ciphertext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encoded - are we expecting an encoded string?
     * @return string
     */
    public static function decrypt($message, $key, $encoded = false)
    {
        if ($encoded) {
            $message = base64_decode($message, true);
            if ($message === false) {
                throw new Exception('Encryption failure');
            }
        }

        $nonceSize = openssl_cipher_iv_length(self::METHOD);
        $nonce = mb_substr($message, 0, $nonceSize, '8bit');
        $ciphertext = mb_substr($message, $nonceSize, null, '8bit');

        $plaintext = openssl_decrypt(
            $ciphertext,
            self::METHOD,
            $key,
            OPENSSL_RAW_DATA,
            $nonce
        );

        return $plaintext;
    }
}

Usage Example

使用示例

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = UnsafeCrypto::encrypt($message, $key);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key);

var_dump($encrypted, $decrypted);

Demo: https://3v4l.org/jl7qR

演示https: //3v4l.org/jl7qR



The above simple crypto library still is not safe to use.We need to authenticate ciphertexts and verify them before we decrypt.

上述简单的加密库仍然不能安全使用。我们需要在解密之前验证密文并验证它们

Note: By default, UnsafeCrypto::encrypt()will return a raw binary string. Call it like this if you need to store it in a binary-safe format (base64-encoded):

注意:默认情况下,UnsafeCrypto::encrypt()将返回原始二进制字符串。如果您需要以二进制安全格式(base64 编码)存储它,请像这样调用它:

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = UnsafeCrypto::encrypt($message, $key, true);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key, true);

var_dump($encrypted, $decrypted);

Demo: http://3v4l.org/f5K93

演示:http: //3v4l.org/f5K93

Simple Authentication Wrapper

简单的身份验证包装器

class SaferCrypto extends UnsafeCrypto
{
    const HASH_ALGO = 'sha256';

    /**
     * Encrypts then MACs a message
     * 
     * @param string $message - plaintext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encode - set to TRUE to return a base64-encoded string
     * @return string (raw binary)
     */
    public static function encrypt($message, $key, $encode = false)
    {
        list($encKey, $authKey) = self::splitKeys($key);

        // Pass to UnsafeCrypto::encrypt
        $ciphertext = parent::encrypt($message, $encKey);

        // Calculate a MAC of the IV and ciphertext
        $mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true);

        if ($encode) {
            return base64_encode($mac.$ciphertext);
        }
        // Prepend MAC to the ciphertext and return to caller
        return $mac.$ciphertext;
    }

    /**
     * Decrypts a message (after verifying integrity)
     * 
     * @param string $message - ciphertext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encoded - are we expecting an encoded string?
     * @return string (raw binary)
     */
    public static function decrypt($message, $key, $encoded = false)
    {
        list($encKey, $authKey) = self::splitKeys($key);
        if ($encoded) {
            $message = base64_decode($message, true);
            if ($message === false) {
                throw new Exception('Encryption failure');
            }
        }

        // Hash Size -- in case HASH_ALGO is changed
        $hs = mb_strlen(hash(self::HASH_ALGO, '', true), '8bit');
        $mac = mb_substr($message, 0, $hs, '8bit');

        $ciphertext = mb_substr($message, $hs, null, '8bit');

        $calculated = hash_hmac(
            self::HASH_ALGO,
            $ciphertext,
            $authKey,
            true
        );

        if (!self::hashEquals($mac, $calculated)) {
            throw new Exception('Encryption failure');
        }

        // Pass to UnsafeCrypto::decrypt
        $plaintext = parent::decrypt($ciphertext, $encKey);

        return $plaintext;
    }

    /**
     * Splits a key into two separate keys; one for encryption
     * and the other for authenticaiton
     * 
     * @param string $masterKey (raw binary)
     * @return array (two raw binary strings)
     */
    protected static function splitKeys($masterKey)
    {
        // You really want to implement HKDF here instead!
        return [
            hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true),
            hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true)
        ];
    }

    /**
     * Compare two strings without leaking timing information
     * 
     * @param string $a
     * @param string $b
     * @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW
     * @return boolean
     */
    protected static function hashEquals($a, $b)
    {
        if (function_exists('hash_equals')) {
            return hash_equals($a, $b);
        }
        $nonce = openssl_random_pseudo_bytes(32);
        return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce);
    }
}

Usage Example

使用示例

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = SaferCrypto::encrypt($message, $key);
$decrypted = SaferCrypto::decrypt($encrypted, $key);

var_dump($encrypted, $decrypted);

Demos: raw binary, base64-encoded

演示原始二进制base64 编码



If anyone wishes to use this SaferCryptolibrary in a production environment, or your own implementation of the same concepts, I strongly recommend reaching out to your resident cryptographersfor a second opinion before you do. They'll be able tell you about mistakes that I might not even be aware of.

如果有人希望SaferCrypto在生产环境中使用这个库,或者你自己实现相同的概念,我强烈建议在你这样做之前联系你的常驻密码学家以获得第二意见。他们将能够告诉你我可能不知道的错误。

You will be much better off using a reputable cryptography library.

使用信誉良好的密码学库会好得多。

回答by Eugen Rieck

Use mcrypt_encrypt()and mcrypt_decrypt()with corresponding parameters. Really easy and straight forward, and you use a battle-tested encryption package.

使用mcrypt_encrypt()mcrypt_decrypt()与相应的参数。非常简单直接,您使用经过实战考验的加密包。

EDIT

编辑

5 years and 4 months after this answer, the mcryptextension is now in the process of deprecation and eventual removal from PHP.

在这个答案之后 5 年零 4 个月,该mcrypt扩展现在正在弃用并最终从 PHP 中删除。

回答by Hemerson Varela

PHP 7.2moved completely away from Mcryptand the encryption now is based on the maintainable Libsodiumlibrary.

PHP 7.2完全远离,Mcrypt现在加密基于可维护Libsodium库。

All your encryption needs can be basically resolved through Libsodiumlibrary.

你所有的加密需求都可以通过Libsodium库基本解决。

// On Alice's computer:
$msg = 'This comes from Alice.';
$signed_msg = sodium_crypto_sign($msg, $secret_sign_key);


// On Bob's computer:
$original_msg = sodium_crypto_sign_open($signed_msg, $alice_sign_publickey);
if ($original_msg === false) {
    throw new Exception('Invalid signature');
} else {
    echo $original_msg; // Displays "This comes from Alice."
}

Libsodium documentation: https://github.com/paragonie/pecl-libsodium-doc

Libsodium 文档:https: //github.com/paragonie/pecl-libsodium-doc

回答by Eugene Fidelin

IMPORTANTthis answer is valid only for PHP 5, in PHP 7 use built-in cryptographic functions.

重要此答案仅对 PHP 5 有效,在 PHP 7 中使用内置加密函数。

Here is simple but secure enough implementation:

这是简单但足够安全的实现:

  • AES-256 encryption in CBC mode
  • PBKDF2 to create encryption key out of plain-text password
  • HMAC to authenticate the encrypted message.
  • CBC 模式下的 AES-256 加密
  • PBKDF2 从纯文本密码中创建加密密钥
  • HMAC 对加密消息进行身份验证。

Code and examples are here: https://stackoverflow.com/a/19445173/1387163

代码和示例在这里:https: //stackoverflow.com/a/19445173/1387163