正确使用php openssl_encrypt的方法

25

我正在做一个涉及密码学的项目,需要一些关于如何使用openssl_encryptopenssl_decrypt的帮助,我只想知道最基本和正确的方法。以下是我目前所得到的:

// To encrypt a string

$dataToEncrypt = 'Hello World';

$cypherMethod = 'AES-256-CBC';
$key = random_bytes(32);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cypherMethod));

$encryptedData = openssl_encrypt($dataToEncrypt, $cypherMethod, $key, $options=0, $iv);

我接下来会保存$cypherMethod$key$iv以供在解密$encryptedData时使用。(不详细说明如何存储这些值,谢谢!)

// To decrypt an encrypted string

$decryptedData = openssl_decrypt($encryptedData, $cypherMethod, $key, $options=0, $iv);
首先,上面的示例代码是否是使用php openssl_encrypt的正确示例?
其次,我生成$key$iv的方法是否正确和安全?因为我一直在读到,密钥应该是具有密码学安全保障的。
最后,对于AES-256-CBC,不是需要32字节的值吗?如果是,那么为什么openssl_cipher_iv_length()返回的长度只有int(16)?难道它不应该是int(32)吗?

先生,我也在研究密码学,您能否详细说明一下如何将值存储在数据库(mySql)中,哪些字段类型适合以及以哪种格式进行存储。我可以成功加密文本并存储值以供以后解密,但是openss_decrypt()方法返回null(我认为存储数据有问题)。非常感谢您的帮助。 - Noman marwat
4个回答

19

首先,上面的示例代码是否是使用php openssl_encrypt的正确示例?

您对该函数的使用看起来是正确的,但是您可能需要考虑使用除CBC之外的其他操作模式。 CBC很难做到正确,因为仅在此模式下加密数据已知存在攻击,例如臭名昭著的CBC比特翻转攻击,它允许攻击者通过修改密文对明文进行有意义的更改。如果可能,我会尽量使用支持GCM的身份验证加密模式(看起来PHP 7.1+中支持(示例#1))。

如果使用CBC模式,请查看文档中的示例#2。请注意,在加密MAC(消息认证码)之后,将计算出MAC并存储在密文中。在解密密文之前应重新计算此MAC,如果与存储的MAC不匹配,则密文已被修改且无效。

其次,我生成$key和$iv的方法是否正确且安全?因为我一直在阅读,密钥应该是密码学安全的。

需要使用密码学安全的随机数生成器生成密钥。幸运的是,大多数操作系统都可以通过/dev/urandom提供此功能。 这篇答案 很好地解释了如何在 PHP 中从/dev/urandom读取。 openssl_random_pseudo_bytes应该也是密码学安全的,但有时候不是这种情况
初始化向量 (IVs) 需要是随机的,并且永远不应该重复使用相同的密钥。

最后,AES-256-CBC需要32字节的值吗?如果是的话,为什么 openssl_cipher_iv_length() 只返回长度为 int(16)?它不应该是 int(32) 吗?

AES是一个分块加密算法,可以对128位(16字节)块进行操作,而与密钥大小无关。

感谢您提供的信息。我有一个快速的问题,我应该使用单个通用密钥还是每次随机生成一个密钥? - Jo E.
您可以使用相同的密钥,但请确保每次调用 openssl_encrypt 时生成一个新的随机 IV。 - puzzlepalace

14

以下是使用openssl_encrypt和openssl_decrypt的最基本方法。确保创建32个字节的secret_key和16个字节的secret_iv。

function encrypt_decrypt($action, $string) 
    {
        $output = false;
        $encrypt_method = "AES-256-CBC";
        $secret_key = 'xxxxxxxxxxxxxxxxxxxxxxxx';
        $secret_iv = 'xxxxxxxxxxxxxxxxxxxxxxxxx';
        // hash
        $key = hash('sha256', $secret_key);    
        // iv - encrypt method AES-256-CBC expects 16 bytes 
        $iv = substr(hash('sha256', $secret_iv), 0, 16);
        if ( $action == 'encrypt' ) {
            $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
            $output = base64_encode($output);
        } else if( $action == 'decrypt' ) {
            $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
        }
        return $output;
    }

4
// --- Encrypt --- //
$key = openssl_digest("passkey", 'SHA256', TRUE);
$plaintext = "Data to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
// binary cipher
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_RAW_DATA, $iv);
// or replace OPENSSL_RAW_DATA & $iv with 0 & bin2hex($iv) for hex cipher (eg. for transmission over internet)

// or increase security with hashed cipher; (hex or base64 printable eg. for transmission over internet)
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );

// --- Decrypt --- //
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, true);
if (hash_equals($hmac, $calcmac))
    echo $original_plaintext."\n";

为什么要从sha256哈希字符串中获取密钥,而不是使用像PBKDF2或Argon2这样的密钥派生方法?在附加hmac时,为什么不使用GCM模式? - Michael Fehr
总有更安全的方法;这个例子说明了一种加密数据以进行存储和传输的方法。更好的加密方式是使用公钥/私钥,而不是一个密钥用于加密和解密。请参见:https://dev59.com/32445IYBdhLWcg3w7uc0 - Zimba

3
   // --- Encrypt --- //
function encrypt($plaintext, $secret_key = "5fgf5HJ5g27", $cipher = "AES-128-CBC")
{

    $key = openssl_digest($secret_key, 'SHA256', TRUE);

    $ivlen = openssl_cipher_iv_length($cipher);
    $iv = openssl_random_pseudo_bytes($ivlen);
    // binary cipher
    $ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_RAW_DATA, $iv);
    // or replace OPENSSL_RAW_DATA & $iv with 0 & bin2hex($iv) for hex cipher (eg. for transmission over internet)

    // or increase security with hashed cipher; (hex or base64 printable eg. for transmission over internet)
    $hmac = hash_hmac('sha256', $ciphertext_raw, $key, true);
    return base64_encode($iv . $hmac . $ciphertext_raw);
}


// --- Decrypt --- //
function decrypt($ciphertext, $secret_key = "5fgf5HJ5g27", $cipher = "AES-128-CBC")
{

    $c = base64_decode($ciphertext);

    $key = openssl_digest($secret_key, 'SHA256', TRUE);

    $ivlen = openssl_cipher_iv_length($cipher);

    $iv = substr($c, 0, $ivlen);
    $hmac = substr($c, $ivlen, $sha2len = 32);
    $ciphertext_raw = substr($c, $ivlen + $sha2len);
    $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, OPENSSL_RAW_DATA, $iv);

    $calcmac = hash_hmac('sha256', $ciphertext_raw, $key, true);
    if (hash_equals($hmac, $calcmac))
        return $original_plaintext . "\n";
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接