EncJDBC es un controlador JDBC que permite a las aplicaciones Java conectarse a una base de datos siempre confidencial y descifrar de forma transparente los datos de columnas cifradas, sin necesidad de modificar la lógica de sus consultas.
Una vez configurada la clave maestra de cifrado (MEK), EncJDBC descifra automáticamente el texto cifrado y devuelve texto sin formato a su aplicación. El proceso de cifrado y descifrado resulta invisible para el código de su aplicación.
Funcionamiento
EncJDBC utiliza un proceso de dos pasos:
Al establecer la conexión, EncJDBC distribuye su MEK al servidor mediante cifrado de sobre, lo que garantiza que la clave nunca se transmita en texto sin formato.
Para cada consulta, EncJDBC cifra automáticamente los parámetros dirigidos a columnas cifradas y descifra los resultados antes de devolverlos a su aplicación.
El código de su aplicación continúa operando con valores en texto sin formato, por lo que no es necesario realizar cambios en la lógica de las consultas.
Requisitos previos
Antes de empezar, asegúrese de contar con lo siguiente:
La función de base de datos siempre confidencial habilitada en su instancia de ApsaraDB RDS for MySQL. Consulte Enable the always-confidential database feature
Los detalles de conexión de la instancia: nombre de host, puerto, nombre de la base de datos, nombre de usuario y contraseña
Una regla de protección de datos configurada. Consulte Configure data protection rules
JDK 1.8 o posterior
Los ejemplos de este tema utilizan Maven 3.9.2 e IntelliJ IDEA Community Edition 2022.3.2.
Almacene su MEK de forma segura y nunca la exponga. Si se pierde, los datos cifrados serán inaccesibles de manera permanente. Su instancia de RDS nunca genera, almacena ni realiza copias de seguridad de su MEK.
Adición de la dependencia de Maven
Agregue la siguiente dependencia al archivo pom.xml de su proyecto:
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-encdb-mysql-jdbc</artifactId>
<version>1.0.9-1</version>
</dependency>
</dependencies>
Configuración y conexión
EncJDBC utiliza el formato de URL JDBC jdbc:mysql:encdb://{hostname}:{port}/{dbname} y la clase de controlador com.aliyun.encdb.mysql.jdbc.EncDriver.
Antes de conectarse, configure dos parámetros obligatorios:
|
Parámetro |
Valor de ejemplo |
Descripción |
|
|
|
La clave maestra de cifrado (MEK) que usted genera y gestiona. Valor válido: una cadena hexadecimal de 32 caracteres (16 bytes). Genere una mediante |
|
|
|
El algoritmo de cifrado. Valor predeterminado: |
Algoritmos de cifrado compatibles:
|
Categoría |
Algoritmos |
|
AES (aceptado internacionalmente) |
|
|
SM4 (estándar nacional chino) |
|
EviteAES_128_ECBySM4_128_ECB, ya que estos modos no ofrecen garantías sólidas de seguridad.
Paso de la configuración a EncJDBC
Existen tres métodos disponibles. Si desea utilizar más de un método para configurar los parámetros en JDBC, estos se priorizan en el siguiente orden: JDBC properties > configuration file > URL parameter.
Puede concatenar varios parámetros mediante &.
Opción 1: JDBC properties
Establezca MEK y ENC_ALGO directamente en el objeto Properties antes de abrir una conexión:
// Replace the placeholders with your instance connection details.
String hostname = "<hostname>";
String port = "<port>";
String dbname = "<dbname>";
String username = "<username>";
String password = "<password>";
String mek = "<your-mek>"; // 32-character hex string
String encAlgo = "<enc-algo>"; // e.g., SM4_128_GCM
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("MEK", mek);
props.setProperty("ENC_ALGO", encAlgo);
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
Connection connection = DriverManager.getConnection(dbUrl, props);
Opción 2: Configuration file
Cree un archivo de configuración con el siguiente contenido:
MEK=<your-mek>
ENC_ALGO=<enc-algo>
Coloque el archivo en una de las siguientes ubicaciones:
El directorio
resourcesde su proyectoEl directorio raíz de su proyecto (el directorio de trabajo en tiempo de ejecución)
El nombre de archivo predeterminado es encjdbc.conf. Para usar una ruta personalizada, establezca la propiedad del sistema encJdbcConfigFile apuntando al directorio del archivo de configuración.
Después de colocar el archivo, conéctese sin especificar MEK ni ENC_ALGO en el código:
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
Connection connection = DriverManager.getConnection(dbUrl, username, password);
Opción 3: URL parameter
Anexe MEK y ENC_ALGO directamente a la URL JDBC:
String dbUrl = String.format(
"jdbc:mysql:encdb://%s:%s/%s?MEK=%s&ENC_ALGO=%s",
hostname, port, dbname, mek, encAlgo
);
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
Connection connection = DriverManager.getConnection(dbUrl, username, password);
Ejemplo completo
El siguiente ejemplo utiliza propiedades JDBC para conectarse, insertar filas en una tabla y consultar los resultados en texto sin formato.
// Replace the placeholders with your instance connection details.
String hostname = "hostname";
String port = "port";
String dbname = "db";
String username = "user";
String password = "password";
// Use a strong, randomly generated MEK in production.
String mek = "00112233445566778899aabbccddeeff";
String encAlgo = "SM4_128_CBC";
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("MEK", mek);
props.setProperty("ENC_ALGO", encAlgo);
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
Connection connection = DriverManager.getConnection(dbUrl, props);
int[] intData = {1, 2, 3, 4, 5, 6};
String[] strData = {"abc", "bcd", "1", "def", "efg", "fgi"};
// Create the table.
connection.createStatement().executeUpdate("drop table if exists test");
connection.createStatement().executeUpdate("create table test (a int, b text)");
// Insert rows using PreparedStatement.
// EncJDBC automatically encrypts values targeting encrypted columns.
for (int i = 0; i < 6; i++) {
PreparedStatement pstmt = connection.prepareStatement("insert into test values (?,?)");
pstmt.setInt(1, intData[i]);
pstmt.setString(2, strData[i]);
pstmt.executeUpdate();
}
// Query rows. EncJDBC automatically decrypts results before returning them.
ResultSet rs = connection.createStatement().executeQuery("select * from test");
while (rs.next()) {
for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
System.out.print(rs.getString(i + 1));
System.out.print("\t");
}
System.out.print("\n");
}
Salida esperada:
1 abc
2 bcd
3 cde
4 def
5 efg
6 fgi
Tenga en cuenta lo siguiente:
Todas las lecturas y escrituras en columnas cifradas pasan por
PreparedStatement. El controlador cifra automáticamente los parámetros y descifra los resultados, por lo que no es necesario modificar la lógica de sus consultas.Los valores pasados a columnas no cifradas no se ven afectados.
Solución de problemas
IllegalAccessError: cannot access class com.sun.crypto.provider.SunJCE
Este error ocurre porque las versiones más recientes de JDK restringen el acceso entre módulos de forma predeterminada. Agregue la siguiente opción de VM al ejecutar su programa:
--add-exports=java.base/com.sun.crypto.provider=ALL-UNNAMED
failed in mek provision: gcmEncrypt error
Se trata de un problema conocido con Oracle JDK. Utilice una de las siguientes soluciones:
Cambie a Amazon Corretto: una distribución OpenJDK compatible que puede usarse como reemplazo directo y no presenta este problema.
-
Agregue el proveedor de seguridad BouncyCastle a Oracle JDK:
Localice el directorio de instalación de su JDK.
Abra
<jdk-path>/conf/security/java.security.En la sección
List of providers and their preference orders, agregue: ``security.provider.14=org.bouncycastle.jce.provider.BouncyCastleProvider``