首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何加密和解密PHP字符串?

如何加密和解密PHP字符串?
EN

Stack Overflow用户
提问于 2013-05-17 10:57:14
回答 10查看 541K关注 0票数 255

我的意思是:

代码语言:javascript
复制
Original String + Salt or Key --> Encrypted String
Encrypted String + Salt or Key --> Decrypted (Original String)

可能是这样的:

代码语言:javascript
复制
"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"

  • 在PHP中,你如何做到这一点?

尝试使用Crypt_Blowfish,,但对我不起作用。

EN

回答 10

Stack Overflow用户

回答已采纳

发布于 2019-07-29 16:15:43

已更新

PHP 7就绪版本。它使用PHP OpenSSL Library中的openssl_encrypt函数。

代码语言:javascript
复制
class Openssl_EncryptDecrypt {
    function encrypt ($pure_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = openssl_random_pseudo_bytes($ivlen);
        $ciphertext_raw = openssl_encrypt($pure_string, $cipher, $encryption_key, $options, $iv);
        $hmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        return $iv.$hmac.$ciphertext_raw;
    }
    function decrypt ($encrypted_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = substr($encrypted_string, 0, $ivlen);
        $hmac = substr($encrypted_string, $ivlen, $sha2len);
        $ciphertext_raw = substr($encrypted_string, $ivlen+$sha2len);
        $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $encryption_key, $options, $iv);
        $calcmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        if(function_exists('hash_equals')) {
            if (hash_equals($hmac, $calcmac)) return $original_plaintext;
        } else {
            if ($this->hash_equals_custom($hmac, $calcmac)) return $original_plaintext;
        }
    }
    /**
     * (Optional)
     * hash_equals() function polyfilling.
     * PHP 5.6+ timing attack safe comparison
     */
    function hash_equals_custom($knownString, $userString) {
        if (function_exists('mb_strlen')) {
            $kLen = mb_strlen($knownString, '8bit');
            $uLen = mb_strlen($userString, '8bit');
        } else {
            $kLen = strlen($knownString);
            $uLen = strlen($userString);
        }
        if ($kLen !== $uLen) {
            return false;
        }
        $result = 0;
        for ($i = 0; $i < $kLen; $i++) {
            $result |= (ord($knownString[$i]) ^ ord($userString[$i]));
        }
        return 0 === $result;
    }
}

define('ENCRYPTION_KEY', '__^%&Q@$&*!@#$%^&*^__');
$string = "This is the original string!";

$OpensslEncryption = new Openssl_EncryptDecrypt;
$encrypted = $OpensslEncryption->encrypt($string, ENCRYPTION_KEY);
$decrypted = $OpensslEncryption->decrypt($encrypted, ENCRYPTION_KEY);
票数 17
EN

Stack Overflow用户

发布于 2015-05-11 11:12:21

在做进一步的工作之前,请先了解the difference between and ,以及为什么您可能需要经过身份验证的加密,而不只是加密。

要实现经过身份验证的加密,您需要先加密MAC。这个问题的现有答案之一The order of encryption and authentication is very important!犯了这个错误;许多用PHP语言编写的密码库也是如此。

您应该使用avoid implementing your own cryptography,而不是使用由密码学专家编写和审查的安全库。

更新:PHP7.2现在提供Update!为了获得最好的安全性,请将您的系统更新为使用PHP 7.2或更高版本,并且只遵循此答案中的higher建议。

(如果您想要不带PECL的without,则为 );否则...

;不要使用自己的加密技术!

上面链接的两个库都使得在您自己的库中实现经过身份验证的加密变得简单而轻松。

如果您仍然想要编写和部署您自己的密码库,而不是Internet上每个密码学专家的传统智慧,那么这些是您必须采取的步骤。

加密:

在CTR模式下使用AES进行

  1. 加密。您也可以使用GCM (这样就不需要单独的MAC了)。此外,ChaCha20和Salsa20 (由libsodium提供)是流密码,不需要特殊的modes.
  2. Unless。如果您在上面选择了GCM,则应该使用HMAC-SHA-256对密文进行身份验证(或者,对于流密码,Poly1305 --大多数libsodium会为您做这件事)。媒体访问控制应覆盖IV和ciphertext!

解密:

  1. 除非使用Poly1305或GCM,否则请重新计算密文的,并将其与使用hash_equals()发送的MAC进行比较。如果失败,则使用abort.
    1. Decrypt发送消息。

其他设计注意事项:

  1. 永远不会压缩任何内容。密文是不可压缩的;在加密之前压缩明文可能导致信息泄漏(例如,TLS上的犯罪和违规)。
  2. 确保您使用mb_strlen()mb_substr(),使用'8bit'字符集模式来防止mbstring.func_overload问题。
  3. is应该使用CSPRNG生成;如果您使用的是D43CSPRNG>,请勿使用

除非您正在使用AEAD结构、

  • bin2hex()

  • 等,否则
  1. 可能会通过缓存计时泄露有关加密密钥的信息。如果可能,请避免使用它们。

即使你遵循了这里给出的建议,密码学也会出很多问题。总是有一位密码学专家审查你的实现。如果你不够幸运地和你当地大学的密码学学生成为私人朋友,你总是可以尝试Cryptography Stack Exchange论坛寻求建议。

如果你需要对你的实现进行专业的分析,你可以雇佣一个reputable team of security consultants to review your PHP cryptography code (披露:我的雇主)。

重要提示:何时不使用加密

。您希望使用以下密码散列算法之一对它们进行散列:

切勿使用通用散列函数(MD5、SHA256)存储密码。

.这工具不适合这项工作。

使用String的PHP字符串加密示例

如果你使用的是PHP < 7.2或者没有安装slower,你可以使用sodium_compat来达到同样的效果(尽管速度较慢)。

代码语言:javascript
复制
<?php
declare(strict_types=1);

/**
 * Encrypt a message
 * 
 * @param string $message - message to encrypt
 * @param string $key - encryption key
 * @return string
 * @throws RangeException
 */
function safeEncrypt(string $message, string $key): string
{
    if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
        throw new RangeException('Key is not the correct size (must be 32 bytes).');
    }
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
    
    $cipher = base64_encode(
        $nonce.
        sodium_crypto_secretbox(
            $message,
            $nonce,
            $key
        )
    );
    sodium_memzero($message);
    sodium_memzero($key);
    return $cipher;
}

/**
 * Decrypt a message
 * 
 * @param string $encrypted - message encrypted with safeEncrypt()
 * @param string $key - encryption key
 * @return string
 * @throws Exception
 */
function safeDecrypt(string $encrypted, string $key): string
{   
    $decoded = base64_decode($encrypted);
    $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
    
    $plain = sodium_crypto_secretbox_open(
        $ciphertext,
        $nonce,
        $key
    );
    if (!is_string($plain)) {
        throw new Exception('Invalid MAC');
    }
    sodium_memzero($ciphertext);
    sodium_memzero($key);
    return $plain;
}

然后进行测试:

代码语言:javascript
复制
<?php
// This refers to the previous code block.
require "safeCrypto.php"; 

// Do this once then store it somehow:
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';

$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

使Halite -Halite变得更容易

我一直在做的一个项目是一个名为Halite的加密库,它旨在使library更容易和更直观。

代码语言:javascript
复制
<?php
use \ParagonIE\Halite\KeyFactory;
use \ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto;

// Generate a new random symmetric-key encryption key. You're going to want to store this:
$key = new KeyFactory::generateEncryptionKey();
// To save your encryption key:
KeyFactory::save($key, '/path/to/secret.key');
// To load it again:
$loadedkey = KeyFactory::loadEncryptionKey('/path/to/secret.key');

$message = 'We are all living in a yellow submarine';
$ciphertext = SymmetricCrypto::encrypt($message, $key);
$plaintext = SymmetricCrypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

所有的底层加密都是由libsodium处理的。

使用defuse/php-encryption的示例

代码语言:javascript
复制
<?php
/**
 * This requires https://github.com/defuse/php-encryption
 * php composer.phar require defuse/php-encryption
 */

use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

require "vendor/autoload.php";

// Do this once then store it somehow:
$key = Key::createNewRandomKey();

$message = 'We are all living in a yellow submarine';

$ciphertext = Crypto::encrypt($message, $key);
$plaintext = Crypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

注意:Crypto::encrypt()返回十六进制编码的输出。

加密密钥管理

如果你想使用“密码”,现在就停止吧。您需要一个随机的128位加密密钥,而不是人类难忘的密码。

您可以存储加密密钥以供长期使用,如下所示:

代码语言:javascript
复制
$storeMe = bin2hex($key);

并且,根据需要,您可以像这样检索它:

代码语言:javascript
复制
$key = hex2bin($storeMe);

我强烈建议只存储一个随机生成的密钥供长期使用,而不是将任何类型的密码作为密钥(或派生密钥)。

如果您使用的是Defuse的库:

“但我真的想使用密码。”

这不是一个好主意,但好吧,这里是如何安全地做它。

首先,生成一个随机密钥并将其存储在一个常量中。

代码语言:javascript
复制
/**
 * Replace this with your own salt! 
 * Use bin2hex() then add \x before every 2 hex characters, like so:
 */
define('MY_PBKDF2_SALT', "\x2d\xb7\x68\x1a\x28\x15\xbe\x06\x33\xa0\x7e\x0e\x8f\x79\xd5\xdf");

请注意,您正在添加额外的工作,并且可以使用此常量作为键,从而省去了许多心痛!

然后使用PBKDF2 (如下所示)从您的密码派生合适的加密密钥,而不是直接使用您的密码进行加密。

代码语言:javascript
复制
/**
 * Get an AES key from a static password and a secret salt
 * 
 * @param string $password Your weak password here
 * @param int $keysize Number of bytes in encryption key
 */
function getKeyFromPassword($password, $keysize = 16)
{
    return hash_pbkdf2(
        'sha256',
        $password,
        MY_PBKDF2_SALT,
        100000, // Number of iterations
        $keysize,
        true
    );
}

不要只使用16个字符的密码。你的加密密钥会被滑稽地破解。

票数 459
EN

Stack Overflow用户

发布于 2017-01-13 06:24:22

我来晚了,但为了寻找正确的方法,我偶然发现了这个页面,它是谷歌搜索返回的顶级页面之一,所以我想分享我对这个问题的看法,我认为它在撰写这篇文章时(2017年初)是最新的。从PHP7.1.0开始,mcrypt_decryptmcrypt_encrypt将被弃用,因此构建未来的验证代码应该使用openssl_encryptopenssl_decrypt

你可以这样做:

代码语言:javascript
复制
$string_to_encrypt="Test";
$password="password";
$encrypted_string=openssl_encrypt($string_to_encrypt,"AES-128-ECB",$password);
$decrypted_string=openssl_decrypt($encrypted_string,"AES-128-ECB",$password);

重要:它使用ECB mode,这是不安全的。如果您想要一个简单的解决方案,而不需要参加密码工程的速成课程,那么不要自己编写,只需使用库即可。

根据您的安全需求,您还可以使用任何其他切片器方法。要找出可用的切片器方法,请参阅openssl_get_cipher_methods函数。

票数 109
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16600708

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档