如何加密和解密一个PHP字符串?

回答 10 浏览 57.7万 2013-05-17

我的意思是,我的意思是。

Original String + Salt or Key --> Encrypted String
Encrypted String + Salt or Key --> Decrypted (Original String)

也许像这样的东西。

"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
  • 在PHP中,如何才能做到这一点呢?

尝试使用Crypt_Blowfish,但对我来说并不奏效。

夏期劇場 提问于2013-05-17
@Rogue 他不想要散列,他想要对称加密(如AES),他只是不知道它叫什么。(现在他知道了:) )Patashu 2013-05-17
它需要有多大的安全性?user557846 2013-05-17
@夏期剧场, 你没有 "加盐 "的对称加密,你使用的是一个密钥。密钥必须是保密的。盐可以是公开的而不损害安全性(只要每个人的盐是不同的),它是一个用于散列密码的术语。Patashu 2013-05-17
你需要一个Salt(私钥)、一个公钥和一种加密算法,如AES-256。wpy.me/blog/15-encrypt-and-decrypt-data in-php-using-aes-256wappy 2015-02-27
@CristianFlorea 那篇博文的作者使用的术语在对称加密的背景下根本没有丝毫意义。AES没有公共密钥,也没有盐。有一个单一的密钥;它必须被保密。在某些操作模式下,有一个不需要保密的IV,但IV不是盐(取决于模式,它可以有相当不同的要求),不需要保密,而实际的加密密钥绝对不能公开。公钥/私钥适用于非对称加密,但与AES没有关系。cpast 2015-03-12
10 个回答
#1楼
得票数 470

在进一步做任何事情之前,请先了解区别encryptionauthentication 之间的区别,以及为什么您可能想要 authenticated encryption 而不仅仅是 encryption

要实现经过身份验证的加密,您需要先加密,然后再加密 MAC。 加密和认证的顺序很重要!这个问题的现有答案之一犯了这个错误;许多用 PHP 编写的密码库也是如此。

您应该避免实施您自己的加密技术,而应该使用由加密专家编写并审查的安全库。

更新:PHP 7.2 现在提供 libsodium!为了获得最佳的安全性,请更新您的系统以使用 PHP 7.2 或更高版本,并只遵循本答案中的 libsodium 建议。

如果您有 PECL 访问权限,请使用 libsodium(或 sodium_compat 如果你想要没有 PECL 的 libsodium);否则……
使用 defuse/php-encryption;不要使用自己的密码学!

上面链接的两个库都可以轻松地在你自己的库中实现验证式加密。

如果你仍然想编写和部署你自己的密码学库,违背互联网上每一位密码学专家的传统智慧,这些是你必须采取的步骤。

Encryption:

  1. 在 CTR 模式下使用 AES 加密。您也可以使用 GCM(无需单独的 MAC)。此外,ChaCha20 和 Salsa20(由 libsodium 提供)是流密码,不需要特殊模式。
  2. 除非您在上面选择 GCM,否则您应该使用 HMAC-SHA-256 验证密文(或者,对于流密码,Poly1305 - 大多数 libsodium API 都会为您执行此操作)。 MAC 应该涵盖 IV 以及密文!

Decryption:

  1. 除非使用Poly1305或GCM,否则重新计算密文的MAC,并与使用hash_equals()发送的MAC进行比较。如果失败,则放弃。
  2. 对信息进行解密。

Other Design Considerations:

  1. 永远不要压缩任何东西。加密文本是不可压缩的;在加密前压缩明文会导致信息泄露(例如 TLS 上的 CRIME 和 BREACH)。
  2. 确保你使用mb_strlen()mb_substr(),使用'8bit'字符集模式,以防止mbstring.func_overload问题。
  3. IV应该使用CSPRNG生成;如果你使用mcrypt_create_iv()不要使用MCRYPT_RAND
  4. 还可以查看random_compat.
  5. 除非你使用AEAD结构,否则总是先加密后MAC!
  6. bin2hex()base64_encode()等可能会通过缓存时间泄露你的加密密钥信息。如果可能的话,要避免它们。

即使您遵循此处给出的建议,密码学也可能会出现很多问题。 始终让密码学专家审查您的实施。如果您没有幸与当地大学的密码学学生成为私人朋友,您可以随时尝试 Cryptography Stack Exchange 论坛寻求建议。

如果您需要对您的实施进行专业分析,您可以随时聘请有信誉的安全顾问团队来审查您的 PHP 密码学代码(披露:我的雇主)。

Important: When to Not Use Encryption

@不要@加密@密码@。你要用这些密码隐藏算法中的一种来代替hash它们:

千万不要使用通用的哈希函数(MD5、SHA256)来存储密码。

不加密URL 参数这是错误的工作工具。

PHP String Encryption Example with Libsodium

如果您使用的是PHP < 7.2或者没有安装libsodium,您可以使用sodium_compat来完成同样的结果(尽管速度较慢)。

<?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;
}

然后再去测试一下。

<?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 - Libsodium Made Easier

我一直在做的一个项目是一个名为Halite的加密库,其目的是让libsodium变得更简单、更直观。

<?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处理的。

Example with defuse/php-encryption

<?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()返回十六进制编码的输出。

Encryption Key Management

如果你想使用"密码",现在就停止吧。你需要一个随机的128位加密密钥,而不是一个人类可记忆的密码。

你可以像这样存储一个加密密钥,以便长期使用。

$storeMe = bin2hex($key);

而且,根据需要,你可以像这样检索它。

$key = hex2bin($storeMe);

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

如果你使用的是Defuse的库。

"But I really want to use a password."

这是个坏主意,但好吧,这里是如何安全地做到这一点的。

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

/**
 * 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(像这样)从你的密码中推导出一个合适的加密密钥,而不是直接用你的密码加密。

/**
 * 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个字符的密码。你的加密密钥将被滑稽地破解。

Scott Arciszewski 提问于2015-05-11
biziclop 修改于2022-08-25
不要对密码进行加密,用password_hash()对其进行散列,用password_verify()对其进行检查。Scott Arciszewski 2015-12-03
@ScottArciszewski 我喜欢你关于键的评论"//做一次,然后以某种方式存储:" ......以某种方式,笑:))那么,将这个'键'(即对象)存储为普通字符串,硬编码如何?我需要钥匙本身,作为一个字符串。我可以从这个对象中得到它吗?谢谢Andrew 2016-04-25
1.2.1版:通过bin2hex()保存。2.0.0版:等它完成后再阅读文档。在版本2发布之前不要使用。Scott Arciszewski 2016-04-25
谢谢,我只需修改一行,就能使你的钠的例子发挥作用。function getKeyFromPassword($password, $keysize = \Sodium\CRYPTO_SECRETBOX_KEYBYTES)Alexey Ozerov 2016-12-29
Wow!+提到不要对URL参数进行加密,这一点值得称赞。我真的很喜欢你提供的这篇文章。paragonie.com/blog/2015/09/…Lynnell Neri 2018-09-28
#2楼
得票数 116

我迟到了,但在寻找正确的方法时,我发现了这个页面,它是谷歌搜索返回的热门之一,所以我想分享我对这个问题的看法,我认为在撰写本文时(2017 年初)它是最新的。从 PHP 7.1.0 开始,mcrypt_decryptmcrypt_encrypt 将被弃用,因此构建未来证明代码应使用 openssl_encryptopenssl_decrypt

你可以做一些事情,比如。

$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模式,不安全。如果您想要一个简单的解决方案,而无需参加密码工程速成课程,请不要自己编写,只需使用库即可。

您也可以使用任何其他削片方法,具体取决于您的安全需求。要了解可用的芯片方法,请参阅 openssl_get_cipher_methods功能。

Emil Borconi 提问于2017-01-12
Bhargav Rao 修改于2019-07-26
谢谢你,我很惊讶这个简单明了的答案没有得到更多的支持。我宁愿不看像上面的答案那样的10页讨论,而我只想对一个简单的字符串进行加密/解密。laurent 2017-04-11
这不是一个安全的答案。ECB模式不应该被使用。如果你想要一个 "简单明了的答案",只要使用一个库Scott Arciszewski 2017-04-11
@ScottArciszewski, yes I admit I've spoken too quickly while looking for some simple code.此后,我在自己的代码中增加了一个IV,并使用CBC,这对我的使用来说已经足够了。laurent 2017-04-12
这完全取决于使用情况!有些情况下,这样做是完全没有问题的。例如,我想在页面之间传递一个GET参数,比方说prod_id=123,但我不想让每个人都能读到123,然而即使他们能读到,也不会有问题。攻击者能够将123替换成一个自定义的值不会造成任何伤害,他/她只能获得其他产品的详细信息,但乔-普通用户不会知道如何获得产品124的详细信息。对于像这样的情况,这是一个完美的解决方案,但对于安全来说,这是一个不可能的事情。Emil Borconi 2017-04-12
"例如,我想在页与页之间传递一个GET参数,比方说prod_id=123,但我就是不想让所有人都能读懂123,然而即使他们能读懂,也不会有问题。"- 我可以建议一个更好的方法吗?paragonie.com/blog/2015/09/…Scott Arciszewski 2017-04-12
#3楼
得票数 45

不应该做什么

警告:
本答案使用ECB。ECB不是一种加密模式,它只是一个构建模块。在本答案中演示的使用ECB实际上并没有对字符串进行安全加密。请不要在你的代码中使用ECB。请参阅Scott的答案,了解一个好的解决方案。

我自己弄的。其实我在谷歌上找到了一些答案,只是修改了一些东西。 然而结果是完全不安全的。

<?php
define("ENCRYPTION_KEY", "!@#$%^&*");
$string = "This is the original data string!";

echo $encrypted = encrypt($string, ENCRYPTION_KEY);
echo "<br />";
echo $decrypted = decrypt($encrypted, ENCRYPTION_KEY);

/**
 * Returns an encrypted & utf8-encoded
 */
function encrypt($pure_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
    return $encrypted_string;
}

/**
 * Returns decrypted original string
 */
function decrypt($encrypted_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
    return $decrypted_string;
}
?>
夏期劇場 提问于2013-05-17
zessx 修改于2015-05-11
你可以使用 "MCRYPT_MODE_CBC",而不是 "MCRYPT_MODE_ECB",以确保更多的安全性。Parixit 2013-08-17
Ramesh,这是因为你得到的是原始加密数据。你可以通过使用base64得到一个更好的加密数据版本,像这样。base64_encode(encrypt($string)) -- 要解密它。decrypt(base64_decode($encrypted))mendezcode 2014-07-31
警告:这是不安全的。ECB模式不应该用于字符串,ECB不需要IV,这没有经过验证,它使用了一个旧的密码(Blowfish)而不是AES,密钥不是二进制的等等。SO社区真的应该停止给那些只是 "有效 "的加密/解密投票,而开始给那些已知的安全的答案投票。如果你不确定这一点,就不要投票。Maarten Bodewes 2015-02-03
我已经这样做了,把mcrypt_encrypt的样本代码替换了。php.net/manual/en/function.mcrypt-encrypt.php。请注意,现在回顾一下,它可能应该有一个rtrim来代替最后的字符"\0"Maarten Bodewes 2015-02-04
正确的答案是使用类似defuse/php-encryption这样的东西,而不是编写你自己的mcrypt代码。Scott Arciszewski 2015-05-11
#4楼
得票数 25

For Laravel framework

如果你使用的是Laravel框架,那么用内部函数来加密和解密就比较容易了。

$string = 'Some text to be encrypted';
$encrypted = \Illuminate\Support\Facades\Crypt::encrypt($string);
$decrypted_string = \Illuminate\Support\Facades\Crypt::decrypt($encrypted);

var_dump($string);
var_dump($encrypted);
var_dump($decrypted_string);

注意:请确保在config/app.php文件的key选项中设置一个16、24或32字符的随机字符串。否则,加密的值将是不安全的。

Somnath Muluk 提问于2015-12-14
Community 修改于2020-06-20
当然,它可能很容易使用。但它安全吗?它是如何解决stackoverflow.com/a/30159120/781723中的问题的?它是否使用了验证的加密技术?它是否避免了旁门左道的漏洞并确保恒定时间的平等检查?它是否使用真正的随机密钥而不是密码/口令?它是否使用合适的操作模式?它是否正确地生成随机IV?D.W. 2016-12-13
#5楼 已采纳
得票数 21

Updated

PHP 7 就绪版本。它使用 PHP OpenSSL 库.

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);
夏期劇場 提问于2019-07-29
夏期劇場 修改于2019-07-29
这是个更好、更安全的版本。谢谢,它也像预期的那样工作。user11856197 2019-07-30
你如何创建并在哪里保存加密密钥?user2800464 2020-04-12
但是,加密文本每次都会改变,我怎样才能将存储的加密字符串与之进行比较呢?qɐʇǝɥɐW 2020-07-08
好了!我明白了,我已经把openssl_random_pseudo_bytes函数改为静态的16个字符的字符串。谢谢你!!!"。qɐʇǝɥɐW 2020-07-08
我在我的项目中使用了这个,并做了一些修改,以便静态地使用它。谢谢你!!!!Carlos Espinoza 2020-12-24
#6楼
得票数 10

如果你不想使用库(你应该这样做),那么就使用像这样的东西(PHP 7)。

function sign($message, $key) {
    return hash_hmac('sha256', $message, $key) . $message;
}

function verify($bundle, $key) {
    return hash_equals(
      hash_hmac('sha256', mb_substr($bundle, 64, null, '8bit'), $key),
      mb_substr($bundle, 0, 64, '8bit')
    );
}

function getKey($password, $keysize = 16) {
    return hash_pbkdf2('sha256',$password,'some_token',100000,$keysize,true);
}

function encrypt($message, $password) {
    $iv = random_bytes(16);
    $key = getKey($password);
    $result = sign(openssl_encrypt($message,'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv), $key);
    return bin2hex($iv).bin2hex($result);
}

function decrypt($hash, $password) {
    $iv = hex2bin(substr($hash, 0, 32));
    $data = hex2bin(substr($hash, 32));
    $key = getKey($password);
    if (!verify($data, $key)) {
      return null;
    }
    return openssl_decrypt(mb_substr($data, 64, null, '8bit'),'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv);
}

$string_to_encrypt='John Smith';
$password='password';
$encrypted_string=encrypt($string_to_encrypt, $password);
$decrypted_string=decrypt($encrypted_string, $password);
Ascon 提问于2018-05-16
Ascon 修改于2018-05-16
这是否可以替代stackoverflow.com/a/16606352/126833?我一直在使用前者,直到我的主机升级到 PHP 7.2。anjanesh 2018-07-23
@anjanesh 你将无法用这个解密旧的数据(不同的算法+这个也会检查签名)。Ascon 2018-07-24
在你的情况下,可能应该这样做。define("ENCRYPTION_KEY", "123456*"); $string = "This is the original data string!"; $encrypted = openssl_encrypt($string, 'BF-ECB', ENCRYPTION_KEY); $decrypted = openssl_decrypt($encrypted,'BF-ECB',ENCRYPTION_KEY);Ascon 2018-07-24
这是超级的!!!!"。anjanesh 2018-07-24
#7楼
得票数 10

这些是使用AES256 CBC在PHP中加密/解密字符串的精简方法。

function encryptString($plaintext, $password, $encoding = null) {
    $iv = openssl_random_pseudo_bytes(16);
    $ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, $iv);
    $hmac = hash_hmac('sha256', $ciphertext.$iv, hash('sha256', $password, true), true);
    return $encoding == "hex" ? bin2hex($iv.$hmac.$ciphertext) : ($encoding == "base64" ? base64_encode($iv.$hmac.$ciphertext) : $iv.$hmac.$ciphertext);
}

function decryptString($ciphertext, $password, $encoding = null) {
    $ciphertext = $encoding == "hex" ? hex2bin($ciphertext) : ($encoding == "base64" ? base64_decode($ciphertext) : $ciphertext);
    if (!hash_equals(hash_hmac('sha256', substr($ciphertext, 48).substr($ciphertext, 0, 16), hash('sha256', $password, true), true), substr($ciphertext, 16, 32))) return null;
    return openssl_decrypt(substr($ciphertext, 48), "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, substr($ciphertext, 0, 16));
}

使用方法。

$enc = encryptString("mysecretText", "myPassword");
$dec = decryptString($enc, "myPassword");

EDIT:这是一个新版本的函数,使用 AES256 GCMPBKDF2 作为密钥派生,更安全。

function str_encryptaesgcm($plaintext, $password, $encoding = null) {
    if ($plaintext != null && $password != null) {
        $keysalt = openssl_random_pseudo_bytes(16);
        $key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm"));
        $tag = "";
        $encryptedstring = openssl_encrypt($plaintext, "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag, "", 16);
        return $encoding == "hex" ? bin2hex($keysalt.$iv.$encryptedstring.$tag) : ($encoding == "base64" ? base64_encode($keysalt.$iv.$encryptedstring.$tag) : $keysalt.$iv.$encryptedstring.$tag);
    }
}

function str_decryptaesgcm($encryptedstring, $password, $encoding = null) {
    if ($encryptedstring != null && $password != null) {
        $encryptedstring = $encoding == "hex" ? hex2bin($encryptedstring) : ($encoding == "base64" ? base64_decode($encryptedstring) : $encryptedstring);
        $keysalt = substr($encryptedstring, 0, 16);
        $key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
        $ivlength = openssl_cipher_iv_length("aes-256-gcm");
        $iv = substr($encryptedstring, 16, $ivlength);
        $tag = substr($encryptedstring, -16);
        return openssl_decrypt(substr($encryptedstring, 16 + $ivlength, -16), "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag);
    }
}

使用方法。

$enc = str_encryptaesgcm("mysecretText", "myPassword", "base64"); // return a base64 encrypted string, you can also choose hex or null as encoding.
$dec = str_decryptaesgcm($enc, "myPassword", "base64");
Marco Concas 提问于2020-06-03
Marco Concas 修改于2022-01-06
#8楼
得票数 8

历史说明:这是在PHP4的时候写的。这就是我们现在所说的 "遗留代码"。

我为历史目的留下了这个答案--但有些方法现在已经被废弃了,DES加密方法不是推荐的做法,等等。

我没有更新这段代码有两个原因。1)我不再在PHP中手工操作加密方法,2)这段代码仍然符合它的初衷:展示在PHP中如何进行加密的最低限度的、简单的概念。

如果你找到一个类似的简单化、"傻瓜式的PHP加密 "之类的资料,可以让人们在10-20行或更少的代码中开始使用,请在评论中告诉我。

除此之外,请欣赏这段早期PHP4极简加密答案的经典剧情。


你肯定有--或者可以得到--mcrypt PHP库,因为它肯定很受欢迎,对各种任务非常有用。下面是不同种类的加密方法和一些示例代码。PHP中的加密技术

//Listing 3: Encrypting Data Using the mcrypt_ecb Function 

<?php 
echo("<h3> Symmetric Encryption </h3>"); 
$key_value = "KEYVALUE"; 
$plain_text = "PLAINTEXT"; 
$encrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $plain_text, MCRYPT_ENCRYPT); 
echo ("<p><b> Text after encryption : </b>"); 
echo ( $encrypted_text ); 
$decrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $encrypted_text, MCRYPT_DECRYPT); 
echo ("<p><b> Text after decryption : </b>"); 
echo ( $decrypted_text ); 
?> 

几条警告。

1)当单向散列值可以使用时,千万不要使用可逆或 "对称 "的加密。

2)如果数据是真正敏感的,如信用卡或社会安全号码,停止;你需要比任何简单的代码块将提供更多,而是你需要一个为这个目的设计的加密库和大量的时间来研究必要的方法。此外,软件加密可能是敏感数据安全的<10%。这就像给核电站重新布线一样--接受这个任务是危险和困难的,如果是这样的话,就超出了你的知识范围。经济上的惩罚可能是巨大的,所以最好使用一个服务,把责任交给他们。

3) 这里列出的任何一种容易实现的加密方式,都可以合理地保护你希望不被窥视的轻度重要信息,或者在意外/故意泄漏的情况下限制暴露。但是,鉴于密钥是以明文形式存储在网络服务器上的,如果他们能得到数据,就能得到解密密钥。

尽管如此,祝你玩得开心。)

BrianH 提问于2013-05-17
zessx 修改于2015-05-11
谢谢!但有些问题。我得到了M�������f=�_=的奇怪字符作为加密的字符。我不能得到简单的字符吗?比如说。2a2ffa8f13220befbe30819047e23b2c.另外,我不能改变$key_value的长度(固定为8?)和输出$encrypted_text的长度吗?夏期劇場 2013-05-17
@夏期剧场 加密的结果是二进制数据。如果你需要它是人类可读的,请对它使用base64或十六进制编码。'我不能改变密钥值的长度吗?不同的对称加密算法对密钥值有不同的要求。'以及输出的长度...'加密文本的长度必须至少与原始文本一样长,否则就没有足够的信息来重新创建原始文本。(这是鸽子洞原理的一个应用)。DES很容易被破解,不再安全。Patashu 2013-05-17
评论指2)如果数据真的很敏感:你能链接一个加密库吗?有什么服务可以让我 "运送责任"?我在网上搜索了一下,但我似乎不能打出正确的关键词来获得好的结果。jnhghy - Alexandru Jantea 2015-01-08
从 PHP 5.5.0 开始,mcrypt_ecb 已经被 DEPRECATED。php.net/manual/en/function.mcrypt-ecb.phpHafez Divandari 2015-01-09
@BrianDHall 这仍然得到低票的原因是ECB模式并不安全(使用CBC、CTR、GCM或Poly1305),DES很弱(你想要AES,这是MCRYPT_RIJNDAEL_128),而且密码文本应该被验证(hash_hmac(),用hash_equals()验证)。Scott Arciszewski 2015-05-11
#9楼
得票数 1

在PHP中,字符串的加密和解密是可以使用一个叫做OpenSSL函数的密码学扩展进行加密和解密的。

openssl_encrypt()函数。openssl_encrypt()函数是用来加密数据的。

语法如下::

string openssl_encrypt(string $data, string $method, string $key, $options = 0, string $iv, string $tag= NULL, string $aad, int $tag_length = 16)

参数如下:。

$data:它持有需要被加密的字符串或数据。

$method:使用openssl_get_cipher_methods()函数来采用密码方法。

$key:它保存了加密密钥。

$options:它持有OPENSSL_RAW_DATA和OPENSSL_ZERO_PADDING这两个标志的位数相加。

$iv:它持有初始化向量,该向量不是NULL。

$tag:它持有认证标签,当使用AEAD密码模式(GCM或CCM)时,它是通过引用来传递的。

$aad:它保存了额外的认证数据。

$tag_length:它持有认证标签的长度。对于GCM模式,认证标签的长度在4到16之间。

返回值:它在成功时返回加密的字符串,在失败时返回FALSE。

openssl_decrypt()函数 openssl_decrypt()函数用于解密数据。

语法如下::

string openssl_decrypt( 字符串 $data, 字符串 $method, 字符串 $key, int $options = 0, 字符串 $iv, 字符串 $tag, 字符串 $aad)

参数如下:。

$data:它持有需要被加密的字符串或数据。

$method:使用openssl_get_cipher_methods()函数来采用密码方法。

$key:它保存了加密密钥。

$options:它持有OPENSSL_RAW_DATA和OPENSSL_ZERO_PADDING这两个标志的位数相加。

$iv:它持有初始化向量,该向量不是NULL。

$tag:它持有使用AEAD密码模式(GCM或CCM)的认证标签。当认证失败时,openssl_decrypt()返回FALSE。

$aad:它保存了额外的认证数据。

返回值:它在成功时返回被解密的字符串,在失败时返回FALSE。

方法。首先声明一个字符串并将其存储到变量中,然后使用openssl_encrypt()函数对给定的字符串进行加密,使用openssl_decrypt()函数对给定的字符串进行解密。

您可以在以下网站上找到实例: https://www.geeksforgeeks.org/how-to-encrypt-and-decrypt-a-php-string/。

Yash 提问于2020-09-18
#10楼
得票数 0

下面的代码在php中对所有带有特殊字符的字符串起作用

   // Encrypt text --

    $token = "9611222007552";

      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);  
      $enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method));  
      $crypted_token = openssl_encrypt($token, $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv);
    echo    $crypted_token;
    //unset($token, $cipher_method, $enc_key, $enc_iv);

    // Decrypt text  -- 

    list($crypted_token, $enc_iv) = explode("::", $crypted_token);  
      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);
      $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv));
    echo   $token;
    //unset($crypted_token, $cipher_method, $enc_key, $enc_iv);
Javed 提问于2019-07-19