【问题标题】:AES encryption in mysql and phpmysql和php中的AES加密
【发布时间】:2023-03-15 13:50:02
【问题描述】:

Mysql里面有个函数AES_encrypt。

SELECT AES_encrypt( "Hello World", "password" ) AS encrypted_value 

这给出了结果:9438eb79863e7009722fc3f0ad4b7198

但是当我使用 php 中的代码进行 AES_encrypt 时,它给了我一个不同的值。

我从 stackoverflow 得到的 PHP 代码 -- PHP AES encrypt / decrypt

<?php
base64_encode(
        mcrypt_encrypt(
            MCRYPT_RIJNDAEL_256,
            $sSecretKey, $sValue, 
            MCRYPT_MODE_ECB, 
            mcrypt_create_iv(
                mcrypt_get_iv_size(
                    MCRYPT_RIJNDAEL_256, 
                    MCRYPT_MODE_ECB
                ), 
                MCRYPT_RAND)
            )
        ), "\0"
?>

PHP 代码的结果是ytip2sEkD87gmRk3IVI09qE7T+RoLr20YK4rJp16NkY=

php 或 codeigniter 中是否有方法可以返回相同的值。? --谢谢。

【问题讨论】:

  • 第一个是HEX格式,第二个是base64?
  • 一方面,mysql 使用 128 位 aes,而您在 php 代码中使用 256 位。 Reference

标签: php mysql codeigniter encryption


【解决方案1】:

您使用的代码存在三个问题:

  1. 正如其他人所提到的,您的 PHP 代码当前使用的是 MCRYPT_RIJNDAEL_256,而正如 AES_ENCRYPT() 下所述:

    使用了 128 位密钥长度的编码,但您可以通过修改源将其扩展到 256 位。我们选择了 128 位,因为它速度更快,并且对于大多数用途来说足够安全。

  2. 正如其他人所提到的,您正在应用 base64_encode() 将 PHP 的二进制结果转换为文本,而 MySQL 结果似乎只是其二进制结果的十六进制表示。从 v5.6.1 起,您可以在 MySQL 中使用 TO_BASE64(),也可以在 PHP 中使用 bin2hex()

  3. mcrypt_encrypt() 中所述:

    如果数据的大小不是 n * blocksize,则数据将用 '\0' 填充。

    而 MySQL 使用 PKCS7 padding

因此,要在 PHP 中获得与您当前在 MySQL 中显示的结果相同的结果:

<?php

class MySQL_Function {
  const PKCS7 = 1;

  private static function pad($string, $mode, $blocksize = 16) {
    $len = $blocksize - (strlen($string) % $blocksize);
    switch ($mode) {
      case self::PKCS7:
        $padding = str_repeat(chr($len), $len); break;

      default:
        throw new Exception();
    }
    return $string.$padding;
  }

  public static function AES_ENCRYPT($str, $key_str) {
    return mcrypt_encrypt(
      MCRYPT_RIJNDAEL_128,
      $key_str, self::pad($str, self::PKCS7),
      MCRYPT_MODE_ECB
    );
  }
}

echo bin2hex(MySQL_Function::AES_encrypt( "Hello World", "password" ));

?>

【讨论】:

    【解决方案2】:

    mcrypt_encrypt 已被弃用,因此这里有一个能够改用openssl_encrypt 的解决方案。说实话,我不知道这一切是如何运作的。这是我发现的一些解决方案的组合,这些解决方案涉及在mcrypt_encrypt 中复制MySQL 的AES_ENCRYPT,然后在openssl_encrypt 中复制mcrypt_encrypt。从否则将用作AES_ENCRYPT 中的盐参数的密钥的生成,以及了解何时使用哪个密码,有点超出我的理解。但我可以说这些函数已经过时间检验,在功能上与 MySql 对应的函数相同。

    if (!function_exists('mysql_aes_key')) {
        /**
         * @param string $key
         * @return string
         */
        function mysql_aes_key($key)
        {
            $new_key = str_repeat(chr(0), 16);
            for ($i = 0, $len = strlen($key); $i < $len; $i++) {
                $new_key[$i % 16] = $new_key[$i % 16] ^ $key[$i];
            }
            return $new_key;
        }
    }
    
    if (!function_exists('aes_encrypt')) {
        /**
         * @param string $val
         * @param string $cypher
         * @param bool $mySqlKey
         * @return string
         * @throws \BadFunctionCallException
         */
        function aes_encrypt($val, $cypher = null, $mySqlKey = true)
        {
            $salt = getenv('SALT') ?: '1234567890abcdefg';
            $key = $mySqlKey ? mysql_aes_key($salt) : $salt;
    
            if (function_exists('mcrypt_encrypt')) {
                $cypher = (!$cypher || $cypher == strtolower('aes-128-ecb')) ? MCRYPT_RIJNDAEL_128 : $cypher;
                $pad_value = 16 - (strlen($val) % 16);
                $val = str_pad($val, (16 * (floor(strlen($val) / 16) + 1)), chr($pad_value));
                return @mcrypt_encrypt($cypher, $key, $val, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_DEV_URANDOM));
            } elseif (function_exists('openssl_encrypt')) {
                //TODO: Create a more comprehensive map of mcrypt <-> openssl cyphers
                $cypher = (!$cypher || $cypher == MCRYPT_RIJNDAEL_128) ? 'aes-128-ecb' : $cypher;
                return openssl_encrypt($val, $cypher, $key, true);
            }
    
            throw new \BadFunctionCallException('No encryption function could be found.');
        }
    }
    
    if (!function_exists('aes_decrypt')) {
        /**
         * @param string $val
         * @param string $cypher
         * @param bool $mySqlKey
         * @return string
         * @throws \BadFunctionCallException
         */
        function aes_decrypt($val, $cypher = null, $mySqlKey = true)
        {
            $salt = getenv('SALT') ?: '1234567890abcdefg';
            $key = $mySqlKey ? mysql_aes_key($salt) : $salt;
    
            if (function_exists('mcrypt_decrypt')) {
                $cypher = (!$cypher || $cypher == strtolower('aes-128-ecb')) ? MCRYPT_RIJNDAEL_128 : $cypher;
                $val = @mcrypt_decrypt($cypher, $key, $val, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_DEV_URANDOM));
                return rtrim($val, chr(0)."..".chr(16));
            } elseif (function_exists('openssl_decrypt')) {
                //TODO: Create a more comprehensive map of mcrypt <-> openssl cyphers
                $cypher = (!$cypher || $cypher == MCRYPT_RIJNDAEL_128) ? 'aes-128-ecb' : $cypher;
                return openssl_decrypt($val, $cypher, $key, true);
            }
    
            throw new \BadFunctionCallException('No decryption function could be found.');
        }
    }
    

    所以...

    putenv('SALT=1234567890abcdefg');
    aes_encrypt('some_value') === SELECT AES_ENCRYPT('some_value', '1234567890abcdefg')
    aes_decrypt('some_encrypted_value') === SELECT AES_DECRYPT('some_encrypted_value', '1234567890abcdefg')
    

    我通过使用 php 函数加密一个值并使用 MySQL 解密它来测试这些,反之亦然。

    【讨论】:

      【解决方案3】:

      MySQL AES_encrypt 使用 128 位密钥长度 - 参考 here

      而您的 PHP 代码使用 256 位密钥长度。

      要解决此问题,您应该能够使用 'MCRYPT_RIJNDAEL_128' 而不是 256。

      【讨论】:

        【解决方案4】:

        接受的答案有效,但代码很多,这是一个衬里

        function aes_encrypt_str($val,$key){
            return mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$key, $val,MCRYPT_MODE_ECB,mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256,MCRYPT_MODE_ECB),MCRYPT_RAND));
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-06-14
          • 2021-11-15
          • 2016-09-22
          • 2021-04-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多