【问题标题】:AES encryption method equivalent to MySQL aes_encrypt() function等效于 MySQL aes_encrypt() 函数的 AES 加密方法
【发布时间】:2021-11-23 00:21:39
【问题描述】:

我想写一个AES加密方法,应该相当于mysqlaes_encrypt

我试着写,但它不正确,因为 mysql 也没有给出正确的数据。

我应该怎么做才能让它正确?

Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec); 
byte[] encryptedTextBytes = cipher.doFinal(message .getBytes("UTF-8")); 
String k = new String(encryptedTextBytes); 
System.out.println("KKKKK"+k);

【问题讨论】:

  • 请先发布您当前不起作用的代码。
  • 密码密码 = Cipher.getInstance("AES");cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte[] encryptedTextBytes = cipher.doFinal(message .getBytes("UTF-8"));字符串 k = 新字符串(加密文本字节); System.out.println("KKKKK"+k);
  • @user2888996 将来编辑您的问题以添加其他详细信息,例如代码。这次我给你加了。
  • 谢谢,我是新来的,不知道如何正确发帖。以后我会记得的
  • String k = new String(encryptedTextBytes) 不正确。 AES 密码的输出不是字符数据(理想情况下,它与随机数据无法区分)并且不太可能表示有效的 UTF-8 代码点。如果您想将密文存储为String,您应该将字节数组编码为 Base64 或 Hex。

标签: java mysql encryption aes


【解决方案1】:

MySQL 的 AES 实现让很多人头疼。这主要是因为 MySQL 如何处理加密密钥。加密密钥被分成 16 字节的块,MySQL 会将一个块中的字节与前一个块中的字节进行异或。如果用户提供的密钥的长度恰好小于 16 字节,则密钥本质上会用空字节填充以达到 16 字节。这就是 MySQL 的 aes_encrypt() 处理密钥的方式。

还处理要加密的值,通过使用 PKCS7 填充数据。您可以了解有关 PKCS7 @http://en.wikipedia.org/wiki/Padding_%28cryptography%29#PKCS7 的所有信息,但它所做的只是填充输入数据,使其位于 16 字节块中。数据填充的字节等于将添加的填充字节数。

长话短说,您需要像 MySQL 一样处理加密密钥,并使用 PKCS7 填充您的输入数据。

有关 Java 中的示例代码,请参阅 Michael Simmons 的以下帖子: http://info.michael-simons.eu/2011/07/18/mysql-compatible-aes-encryption-decryption-in-java/

【讨论】:

    【解决方案2】:

    几年前我不得不使用 BouncyCastle 来做这件事。正如 Alen Puzic 的回答中所述,这两个问题是 mysql 密钥生成和 PKCS7 填充。 BouncyCastle 将使用他们的PaddedBufferedBlockCipher 为您处理填充,但您需要自己生成密钥。这是执行此操作的代码:

    /**
     * Use password to generate a MySQL AES symmetric key
     * @param passwd Password String to use.
     * @param keyLength Must be evenly divisible by 8.
     * @return Key for use with MySQL AES encrypt/decrypt fuctions.
     */
    public static KeyParameter getMySqlAESPasswdKey(String passwd, int keyLength) {
        byte[] pword = passwd.getBytes();
        byte[] rawKey = new byte[keyLength/8];
        int j = 0;
        for (int i = 0; i < pword.length; i++, j++) {
    
            if(j==rawKey.length) {
                j = 0;
            }
            rawKey[j] = pword[i];
        }
    
        return new KeyParameter(rawKey);
    }
    

    注意mysql的默认keyLength是128。

    使用上面的方法生成KeyParameter就可以完成如下的加解密。

    /**
     * Password based encryption using AES with MySql style key generation.
     * @param toEncrypt Unencrypted byte array.
     * @param key A KeyParameter generated with the getMySqlAESPasswdKey() method.
     * @return Encrypted byte array.
     * @throws InvalidCipherTextException If provided key cannot be used with this method on the provided data.
     */
    public static byte[] mysqlAesPasswdEncrypt (byte [] toEncrypt, KeyParameter key) throws InvalidCipherTextException {
        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new AESFastEngine());
    
        cipher.init(true, key);
        byte[] result = new byte[cipher.getOutputSize(toEncrypt.length)];
        int len = cipher.processBytes(toEncrypt, 0, toEncrypt.length, result, 0);
        cipher.doFinal(result, len);
        return result;
    }
    
    /**
     * Password based decryption using AES with MySql style key generation.
     * @param toDecrypt Encrypted byte array.
     * @param key A KeyParameter generated with the getMySqlAESPasswdKey() method.
     * @return Unencrypted byte array.
     * @throws InvalidCipherTextException If provided key cannot be used with this method on the provided data.
     */
    public static byte[] mysqlAesPasswdDecrypt (byte [] toDecrypt, KeyParameter key) throws InvalidCipherTextException {
        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new AESFastEngine());
    
        cipher.init(false, key);
        byte[] result = new byte[cipher.getOutputSize(toDecrypt.length)];
        int len = cipher.processBytes(toDecrypt, 0, toDecrypt.length, result, 0);
        cipher.doFinal(result, len);
        return stripTrailingZeros(result);
    }
    
    /**
     * Strip trailling zeros from the end of decrypted byte arrays.
     * @param data Data to strip.
     * @return Stripped data.
     */
    public static byte[] stripTrailingZeros(byte[] data) {
        int lastData = data.length-1;
        for (int i = data.length-1; i >= 0; i--) {
            if(data[i]!=(byte)0) {
                lastData = i;
                break;
            }
        }
    
        byte[] data2 = new byte[lastData+1];
        System.arraycopy(data, 0, data2, 0, lastData+1);
        return data2;
    }
    

    【讨论】:

      【解决方案3】:

      感谢指点,终于搞定了,下面是一个简单的python版本:

      from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
      from cryptography.hazmat.backends import default_backend
      from cryptography.hazmat.primitives import padding
      import codecs
      
      
      def encrypt(data, key):
          key = key + b"\0" * (16 - len(key) % 16)
          padder = padding.PKCS7(128).padder()
          data = padder.update(data) + padder.finalize()
          alg = algorithms.AES(key)
          cipher = Cipher(alg, modes.ECB(), default_backend())
          encryptor = cipher.encryptor()
          ct = encryptor.update(data) + encryptor.finalize()
          return ct
      
      
      if __name__ == '__main__':
          enc = encrypt(b'123456', b'1234567890')
          print(codecs.encode(enc, 'hex'))
      

      【讨论】:

        【解决方案4】:

        我建议使用the Bouncy Castle Java 加密 API。 BC 被广泛认为是一个很棒的加密工具包,如果你愿意,它可以作为加密提供者插入到 Java API 中。我知道这并不能直接回答您的问题,但我从未见过有人对 Bouncy Castle 有任何疑问。

        【讨论】:

        • 抱歉,我必须对此投反对票。正如您所承认的,这不是问题的答案。我确信一旦发现问题就可以使用 BouncyCastle,但对于许多 JCE 提供者(包括 Oracle 提供者......)可能也是如此。
        猜你喜欢
        • 1970-01-01
        • 2015-12-29
        • 1970-01-01
        • 2011-06-20
        • 2015-12-07
        • 2014-03-24
        • 2020-10-22
        • 1970-01-01
        相关资源
        最近更新 更多