AES_DECRYPT 无法在 linux 上工作:可能链接到 Hibernate

AES_DECRYPT not working on linux : maybe linked to Hibernate

我在我的数据库中加密了数据,我正在尝试执行一个允许我在 phpmyadmin 中以明文形式显示值的请求。

我使用以下请求:

 SELECT CAST(AES_DECRYPT(`my_encrypted_colum`, UNHEX('pass_in_hexa') AS CHAR) AS clear_value 
 FROM `my_table`

当我在开发环境 (windows) 上使用它时,它运行良好。但是一旦我在生产前环境 (linux) 上使用它,我就会得到所有值的 NULL

我很确定这与不同的环境有关,但我不知道是什么。我什至不知道哪个函数没有按预期运行:UNHEX 或 AES_DECRYPT(我猜是 UNHEX)?

这是我的开发环境和预生产环境的配置:

开发 :

Serveur : localhost via TCP/IP
Type de serveur : MySQL
Version du serveur : 5.6.15-log - MySQL Community Server (GPL)
Version du protocole : 10
Utilisateur : root@localhost
Jeu de caractères du serveur : UTF-8 Unicode (utf8)

Apache/2.2.25 (Win32) PHP/5.3.19
Version du client de base de données : libmysql - mysqlnd 5.0.8-dev -     20102224 - $Id: 65fe78e70ce53d27a6cd578597722950e490b0d0 $
Extension PHP : mysqli 

预生产

Serveur: Localhost via UNIX socket
Logiciel: MySQL
Version du logiciel: 5.6.14 - MySQL Community Server (GPL)
Version du protocole: 10
Utilisateur: root@localhost
Jeu de caractères du serveur: UTF-8 Unicode (utf8)

Apache/2.2.15 (CentOS)
Version du client de base de données: libmysql - 5.1.72
Extension PHP: mysqli 

编辑

我继续我的研究,它缝合方法 AES_DECRYPT 和 UNHEX 无罪。 事实上,如果我直接在 phpMyAdmin 的 table 中添加加密值,如下所示:

 INSERT INTO `my_table` (`my_encrypted_column`) VALUES (AES_ENCRYPT('blabla', UNHEX('pass_in_hexa'))

然后我设法 正确地 检索了之前 SELECT 请求的数据。

这意味着问题一定出在我最初插入数据的方式上。 为此,我使用 Hibernate 和 nullSafeSet 方法。

困扰我的是:如果我保存数据的方式有问题,为什么在Windows上工作而不在Linux[=上工作? 54=]?

下面是我对 nullSafeSet 和 nullSafeGet 的实现

private static final String CIPHER_ALGORITHM = "AES";

// nullSafeSet
protected void noNullSet(PreparedStatement st, Object value, int index, SessionImplementor si) throws SQLException {
    byte[] clearText = ((String) value).getBytes(Charset.forName("UTF-8"));

    try {
        Cipher encryptCipher = Cipher.getInstance(CIPHER_ALGORITHM);
        encryptCipher.init(Cipher.ENCRYPT_MODE, getKey(cle));
        st.setBytes(index, encryptCipher.doFinal(clearText));
    } 
    catch (GeneralSecurityException e) {
        throw new RuntimeException("should never happen", e);
    }
}

@Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor si, Object owner) throws HibernateException, SQLException {
    byte[] bytes = rs.getBytes(names[0]);
    try {
        Cipher decryptCipher = Cipher.getInstance(CIPHER_ALGORITHM);
        decryptCipher.init(Cipher.DECRYPT_MODE, getKey(cle));
        if (bytes != null) {
            return new String(decryptCipher.doFinal(bytes), Charset.forName("UTF-8"));
        } 
        else {
            return new String();
        }

    } 
    catch (GeneralSecurityException e) {
        throw new RuntimeException("Mauvaise clé");
    }
}

private static SecretKeySpec getKey(String secretKey) {
    final byte[] finalKey = new byte[16];
    int i = 0;
    for (byte b : secretKey.getBytes()) {
        // XOR
        finalKey[i++ % 16] ^= b;
    }
    return new SecretKeySpec(finalKey, "AES");
}

您知道可能导致问题的原因吗?

我认为您的系统之间可能存在 chaining/padding 差异,因为您没有明确设置它们。尝试

private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

用于获取您的 Cipher 实例,因为它应该是实现 MySql 按照建议使用 here

如果您的密码长度超过 8 个字符(字节),mysql 会按照建议使用其自己的生成 AES 128 位密钥的实现 here。当您的密码长度超过 8 个字节时,它可能与 Java 使用的密钥不同。

检查:

  • 如果您在这些数据库中有相同的数据。从您的开发人员连接到 preprod 数据库并检查它是否有效。从 preprod 连接到您的开发数据库并检查它是否有效。这样你应该缩小问题范围:db vs environment
  • jvm 加密权限。也许你的 dev jvm 可以使用强加密而 preprod 不能
  • 区分大小写:一些数据库(不确定 mysql)将表存储在文件中。 windows 不区分大小写,linux 不区分大小写。我以前见过这样的问题

您可能遇到了字符集问题。如果您的 MySQL 数据库之一是 运行 latin1 字符集,则插入 UTF-8 数据将导致数据混乱。

要检查有问题的 table 上的字符集,运行 在您的两个系统上执行此命令:

mysql> show create table foo
| foo | CREATE TABLE `foo` (
  `FLD1` int(11) DEFAULT NULL,
  `FLD2` int(11) DEFAULT NULL,
  `FLD3` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 |

它将向您显示特定 table 正在使用的字符集。如果它不是 UTF8,那么它将与您的代码不匹配,您将遇到数据损坏问题。

如果这确实是问题所在,您可以通过 运行 如下所示的 ALTER 语句更改字符集:

ALTER TABLE foo CONVERT TO CHARACTER SET utf8

所以我终于设法指出了问题所在。

在应用程序启动时,我将密钥存储在配置对象中。密钥存储为字符串,执行 String cle = new String(key.getEncoded());

这在 windows 上工作正常并返回给我以下密钥:

*£Ðtôµ•Ã

但在 linux 上,特殊字符未正确转换

*��t����

这导致 encryption/decryption 在 linux 上使用错误的键完成,所以我在执行 "SELECT" 时无法使用 "correct" 键值MySQL