加密
Ebean 支持对特定属性进行透明的加密/解密。我们用 @Encrypted
标记我们想要加密的属性,这些属性将根据需要自动加密和解密。
加密/解密可以在客户端/应用程序端或数据库端进行。当我们使用数据库加密时,我们可以在查询中将这些属性用作 where
和 order by
子句的一部分。实际上,属性的加密对应用程序是完全透明的。
当我们使用客户端/应用程序端加密/解密时,我们应该只在 where
子句中使用 EQ(等于)运算符。
客户端/应用程序加密
使用客户端/应用程序加密时,属性由 Java 函数(io.ebean.config.Encryptor
接口的实现)加密/解密。默认实现使用基于 AES 128 位的实现,我们还可以将 Ebean 配置为使用其他实现。
使用客户端/应用程序加密加密的属性应该仅在 where
子句中与 EQ(等于)运算符一起使用 - 其他运算符不应该与客户端加密属性一起使用。
package io.ebean.config;
/**
* Used for Java side encryption of properties when DB encryption is not used.
*
* By default this is used on non-varchar types such as Blobs.
*/
public interface Encryptor {
/**
* Encrypt the data using the key.
*/
byte[] encrypt(byte[] data, EncryptKey key);
/**
* Decrypt the data using the key.
*/
byte[] decrypt(byte[] data, EncryptKey key);
/**
* Encrypt the formatted string value using a key.
*/
byte[] encryptString(String formattedValue, EncryptKey key);
/**
* Decrypt the data returning a formatted string value using a key.
*/
String decryptString(byte[] data, EncryptKey key);
}
数据库加密
使用数据库端加密/解密时,我们使用数据库存储过程来加密和解密属性。例如,对于 Postgres,Ebean 使用 pgp_sym_encrypt()
和 pgp_sym_decrypt()
。
数据库加密函数
用于每个平台的默认数据库加密解密函数是
- Postgres、YugabyteDB - pgp_sym_decrypt()、pgp_sym_encrypt()
- MySql、MariaDB - aes_encrypt()、aes_decrypt()
- SQL Server - DecryptByPassPhrase()、EncryptByPassPhrase()
- Oracle - 需要 dbms_crypto,并使用自定义函数进行加密和解密
- H2 - 使用“AES”选项进行 encrypt() 和 decrypt()
支持的类型
以下是数据库加密支持的类型。任何不受数据库加密支持的类型都将使用客户端/应用程序加密。
- 枚举(如果基于 VARCHAR)
- 字符串(VARCHAR、CHAR、CLOB、LONGVARCHAR)
- 日期类型 - LocalDate、Date、Joda LocalDate
- 时间戳类型 - Timestamp、Instant、OffsetDateTime、ZonedDateTime
重要提示:当前不支持以下类型
- 基本类型
- 时间戳
EncryptKeyManager
每当对属性进行加密或解密时,都必须使用“密钥”。Ebean 会在内部根据表和列名称向 EncryptKeyManager 索取密钥。
我们必须提供 EncryptKeyManager 的实现。
package io.ebean.config;
/**
* Determine keys used for encryption and decryption.
*/
@FunctionalInterface
public interface EncryptKeyManager {
/**
* Initialise the EncryptKeyManager.
*
* This gives the EncryptKeyManager the opportunity to get keys etc.
*/
default void initialise() {}
/**
* Return the key used to encrypt and decrypt a property mapping to the given
* table and column.
*/
EncryptKey getEncryptKey(String tableName, String columnName);
}
@Encrypted
使用 @Encrypted
注解标记要加密的属性。默认情况下,属性将为 dbEncryption = true
,并且我们明确将其设置为 false 以进行客户端/应用程序端加密。
// use database side encryption
@Encrypted
String name;
// use client side encryption (not db functions)
@Encrypted(dbEncryption=false)
String description;
示例
// Use @Encrypted annotation to mark the encrypted properties
@Entity
@Table(name="patient")
public class Patient {
@Id
long id;
// database side encryption
@Encrypted
String name;
// client side encryption
@Lob
@Encrypted(dbEncryption=false)
String description;
@Encrypted
LocalDate dob;
...
限制
- 使用 Java 客户端加密的属性应仅在 WHERE 子句中使用 EQ(等于)运算符
- H2、Postgres、YugabyteDB、MySql、MariaDB、Sql Server 和 Oracle 内置了数据库加密支持。
- 我们不能对带位置(1、2、3...)的参数使用加密。我们必须使用命名参数或条件 API 来定义查询。
示例
List<Patient> list =
new QPatient()
.name.eq("Rob")
.findList();
在以下 Postgres SQL 中生成结果
select t0.id, pgp_sym_decrypt(t0.name,?)
from patient t0
where pgp_sym_decrypt(t0.name,?) = ?
配置
在 ebean.properties 文件中指定 EncryptKeyManager 实现,如下所示
ebean.encryptKeyManager=org.example.BasicEncyptKeyManager
使用 DatabaseConfig 以编程方式进行配置。
DatabaseConfig config = DatabaseConfig();
...
EncryptKeyManager keyManager = ...;
config.setEncryptKeyManager(keyManager);
...
Database database = DatabaseFactory.create(config);
EncryptKeyManager 的示例为
package org.example.encrypt;
import io.ebean.config.EncryptKey;
import io.ebean.config.EncryptKeyManager;
public class BasicEncyptKeyManager implements EncryptKeyManager {
public void initialise() {
// can load keys or initialise source resources ...
}
public EncryptKey getEncryptKey(String tableName, String columnName) {
// get the key for the given table and column
String keyValue = ...;
return new BasicEncryptKey(keyValue);
}
}
内部
Ebean 会检测何时使用加密属性。它会使用属性的表和列调用 EncryptKeyManager 以获取加密密钥。然后将此密钥作为绑定变量添加到已准备好的语句中。
由于密钥作为绑定变量添加到语句中,因此我们不能对“带位置”的参数使用加密,因为它实际上可以更改其他参数的位置。我们可以使用命名参数或条件 API 来构建查询,但不能使用带位置(1、2、3、4...)的参数。