【问题标题】:Correct way to use php openssl_encrypt正确使用 php openssl_encrypt 的方法
【发布时间】:2017-12-29 05:32:43
【问题描述】:

我正在一个项目中使用密码学,我需要一些关于如何使用 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-byte 值吗?如果是,那么为什么 openssl_cipher_iv_length() 只返回 int(16) 作为长度?不应该是int(32)吗?

【问题讨论】:

  • 先生,我也在研究密码学,您能否详细说明如何将您的值存储在数据库(mySql)中,哪些字段类型是合适的以及您以哪种格式执行此操作。我可以成功加密文本并存储值以供以后解密,但 openss_decrypt() 方法返回 null(我认为存储数据有问题)。任何帮助将不胜感激

标签: php encryption cryptography aes php-openssl


【解决方案1】:
   // --- 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";
}

【讨论】:

    【解决方案2】:

    这里是使用 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;
        }
    

    【讨论】:

      【解决方案3】:

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

      您对该函数的使用看起来是正确的,但是您可能需要考虑除 CBC 之外的其他操作模式。 CBC 很难正确处理,因为仅在这种模式下加密数据就会受到已知攻击,例如臭名昭​​著的CBC bit-flipping attack,它允许攻击者通过修改密文对明文进行有意义的更改。如果可能的话,我会使用像 GCM 这样的经过身份验证的加密模式(看起来像 it's supported in PHP 7.1+ (Example #1))。

      如果您使用 CBC 模式,请查看 Example #2 in the docs。请注意,加密后的 MAC(消息验证码)是通过密文计算并存储的。在解密密文之前应该重新计算这个MAC,如果它与存储的MAC不匹配,那么密文已经被修改并且无效。

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

      需要使用加密安全的随机数生成器生成密钥。幸运的是,大多数操作系统通过/dev/urandom 提供了一个开箱即用的系统。 This answer 很好地解释了如何在 PHP 中读取 /dev/urandomopenssl_random_pseudo_bytes 应该也是加密安全的,但有 times when this is not the case

      初始化向量 (IV) 需要是随机的,并且永远不应使用相同的密钥重复使用。

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

      AES 是一种适用于 128 位(16 字节)块的分组密码,无论密钥大小如何。

      【讨论】:

      • 感谢您提供的信息。只是一个简单的问题,我应该使用单个通用密钥还是应该每次都随机生成它?
      • 您可以使用相同的密钥,只需确保每次调用openssl_encrypt时生成一个新的随机IV。
      【解决方案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-hashed 字符串而不是像 PBKDF2 或 Argon2 这样的密钥派生方法中获取密钥?添加 hmac 时为什么不使用 GCM 模式?
      • 总有更安全的方法;此示例说明了一种加密数据以进行存储和传输的方法。更好的密码学是公钥/私钥,而不是加密和解密的一个密钥。见:stackoverflow.com/questions/4629537/…
      猜你喜欢
      • 2020-10-13
      • 2023-03-12
      • 1970-01-01
      • 2011-07-10
      • 2015-07-04
      • 2017-07-10
      • 2016-09-18
      • 2019-06-16
      • 1970-01-01
      相关资源
      最近更新 更多