3

我想写一个应该等同于 mysql 的 AES 加密方法aes_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);
4

4 回答 4

10

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

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

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

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

于 2013-10-30T20:00:14.490 回答
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);
}

请注意,keyLengthmysql 的默认值为 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;
}
于 2014-10-04T17:47:17.070 回答
0

感谢您的见解,终于做对了,这是一个简单的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'))
于 2017-02-17T08:46:12.007 回答
-1

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

于 2013-10-22T14:31:06.753 回答