集成EncJDBC

更新时间:2025-03-21 02:38:00
重要

本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。

配置RDS MySQL版、RDS PostgreSQL版或PolarDB MySQL版数据库表中敏感数据列加密后,如果希望通过Java应用访问这些加密列的明文,可使用EncJDBC驱动连接数据库。本文介绍如何使用EncJDBC连接数据库并访问加密列的明文数据。

背景信息

列加密功能允许用户对数据库中的特定列进行加密存储,从而提高数据的安全性。加密后的数据在数据库中以密文形式存储,但通过授权的客户端可以透明地解密并访问明文数据。

阿里云提供了Java语言的全密态客户端驱动程序EncJDBC。用户只需在客户端侧使用该驱动连接数据库,并在数据库连接URL中指定主密钥(Master Encryption Key,简称MEK),即可访问加密数据库。该驱动会自动完成密文数据的解密返回明文数据。

MEK:由客户端通过安全的非对称加密协议传输给数据库服务端,使服务端、客户端具有相同的密钥,从而通过对称加密安全传输数据。

取值范围:长度为16字节的16进制字符串,且长度为32个字符。

警告

MEK是您授权客户端访问加密数据的根凭据。出于安全考虑,加密数据库不持有并管理您的MEK,也不提供MEK的生成和备份服务,您需要自行生成MEK。MEK的保存和管理对数据库的安全性非常重要。因此我们建议您妥善备份MEK。

前提条件

  • 已为目标数据库配置列加密能力,并设置目标数据库账号的密文权限为密文权限(JDBC解密)。配置数据库列加密的具体操作和账号权限的详细说明,请参见列加密

  • 已获取加密数据库连接信息:连接地址、端口、数据库名称、数据库账号、密码。

  • 已生成一个MEK,用32位的十六进制字符串表示。例如,00112233445566778899aabbccddeeff。

    常见的生成方法有:密码生成工具或编程语言中的random函数。

    例如:

    • Linux系统自带OpenSSL工具,执行openssl rand -hex 16,生成密钥。

    • Windows系统,请安装OpenSSL软件包

注意事项

  • 请妥善保存您设置的主密钥MEK

  • Java使用JDK 1.8或以上版本。

    说明

    本文中示例使用的Maven版本为3.9.9,使用的开发工具为IntelliJ IDEA Community Edition 2024.1.2

客户端接入说明

在客户端侧将数据库连接驱动改为EncJDBC,更新数据库连接URL,指定密钥MEK,实现对数据库加密列的明文访问。

1. 安装依赖

您需要在自己的Maven项目配置文件pom.xml中加入以下依赖项。

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-cls-jdbc</artifactId>
    <version>1.0.10-1</version>
</dependency>

2. 配置MEK连接数据库

下面介绍配置MEK的方法:JDBC properties配置、文件配置和URL配置。如果您的JDBC同时配置了两种以上的方法,优先级顺序为:JDBC properties配置 > 文件配置 > URL配置。

说明
  • URL配置方式中,多个参数可以用&进行拼接。

  • 以下配置和连接方式,MEK均在客户端本地进行处理,并以安全方式(信封加密)发送到服务端,保证MEK不泄露。

JDBC properties配置
文件配置
URL配置

标准的JDBC在连接时,可以通过Properties设置用户自定义属性。以下是通过这种方式配置JDBC属性并运行JDBC的例子:

// 准备好连接地址(hostname)、端口(port)、数据库实例名(dbname)、用户名(username)、密码(password)等连接信息
// ...

String hostname = "your-hostname";
String port = "your-port";
String dbname = "your-database-name";
String username = "your-username";
String password = "your-password";
String mek = "00112233445566778899aabbccddeeff"; // 用户主密钥

Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("MEK", mek);

// 下面是MySQL版数据库的连接URL格式"jdbc:mysql:encdb://%s:%s/%s"。PostgreSQL版数据库的连接URL格式需替换为"jdbc:postgresql:encdb://%s:%s/%s"
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);

// 下面是MySQL版数据库的加载 EncJDBC 驱动。PostgreSQL版数据库的加载 EncJDBC 驱动替换为"com.aliyun.encdb.postgresql.jdbc.EncDriver"
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");

// 获取数据库连接
Connection connection = DriverManager.getConnection(dbUrl, props);

// ... 发起查询 ...

支持通过配置文件来导入参数,配置需要的MEK等参数。您可以在项目中设置一个名为encJdbcConfigFileproperty,将值设置为配置文件路径即可(缺省时,默认使用encjdbc.conf的文件)。

配置文件的内容如下:

MEK=00112233445566778899aabbccddeeff

您可以通过以下两种方式放置配置文件的位置:

  • 将文件放入项目中的resources目录下,如下图所示:

    image

  • 将文件放入项目根目录,即程序的运行时目录。

通过文件配置方式,在做完文件配置后,您可以不用在程序中做额外的配置,如下面所示:

// 准备好连接地址(hostname)、端口(port)、数据库实例名(dbname)、用户名(username)、密码(password)等连接信息
// ...
String hostname = "your-hostname";
String port = "your-port";
String dbname = "your-database-name";
String username = "your-username";
String password = "your-password";

// 下面是MySQL版数据库的连接URL格式"jdbc:mysql:encdb://%s:%s/%s"。PostgreSQL版数据库的连接URL格式需替换为"jdbc:postgresql:encdb://%s:%s/%s"
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);

// 下面是MySQL版数据库的加载 EncJDBC 驱动。PostgreSQL版数据库的加载 EncJDBC 驱动替换为"com.aliyun.encdb.postgresql.jdbc.EncDriver"
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");

// 获取数据库连接
Connection connection = DriverManager.getConnection(dbUrl, username, password);

// ... 发起查询 ...

支持通过URL链接中嵌入MEK等参数。如下面所示:

// 准备好连接地址(hostname)、端口(port)、数据库实例名(dbname)、用户名(username)、密码(password)等连接信息
// ...
String hostname = "your-hostname";
String port = "your-port";
String dbname = "your-database-name";
String username = "your-username";
String password = "your-password";
String mek = "00112233445566778899aabbccddeeff"; // 用户主密钥

// 下面是MySQL版数据库的连接URL格式"jdbc:mysql:encdb://%s:%s/%s?MEK=%s"。PostgreSQL版数据库的连接URL格式需替换为"jdbc:postgresql:encdb://%s:%s/%s?MEK=%s"
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s?MEK=%s", hostname, port, dbname, mek);

// 下面是MySQL版数据库的加载 EncJDBC 驱动。PostgreSQL版数据库的加载 EncJDBC 驱动替换为"com.aliyun.encdb.postgresql.jdbc.EncDriver"
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");

// 获取数据库连接
Connection connection = DriverManager.getConnection(dbUrl, username, password);

// ... 发起查询 ...

3. 查询加密列的明文数据

连接数据库成功后,可以像普通JDBC查询一样操作数据库。EncJDBC会自动对加密列进行解密并返回明文数据。

示例代码:

// 发起查询

// 创建查询语句
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM your_table_name");

// 遍历结果集
while (resultSet.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");
}

完整代码示例

JDBC properties配置MEK为例,使用具备密文权限(JDBC解密)的数据库账号查询某RDS MySQL数据库中加密列的明文数据。

以下示例中数据库配置的相关信息,请参见验证列加密结果中的RDS MySQL数据库列加密示例

import java.sql.*;
import java.util.Properties;
public class EncryptedColumnAccess {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 以下连接地址(hostname)、端口(port)、数据库实例名(dbname)、用户名(username)、密码(password)等连接信息需要更新为您的实例信息
        String hostname = "rm-******.mysql.rds.aliyuncs.com";
        String port = "3306";
        String dbname = "sddp_em_db";
        String username = "sddp_em03";
        String password = "******";
        
        // 只是示例,建议用更复杂的密钥
        String mek="00112233445566778899aabbccddeeff";

        Properties props = new Properties();
        props.setProperty("user", username);
        props.setProperty("password", password);
        props.setProperty("MEK", mek);

        String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);

        // 加载 EncJDBC 驱动
        Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");

        // 获取数据库连接
        Connection connection = DriverManager.getConnection(dbUrl, props);


        // 发起查询
        try {
            // 创建查询语句
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery("SELECT * FROM users");

            // 遍历结果集
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("username");
                String phone = resultSet.getString("phone");

                // 根据你的表结构处理其他字段
                System.out.println("ID: " + id + ", Name: " + name + ", Phone: " + phone);
            }

            // 关闭资源
            resultSet.close();
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

输出结果示例:

image

  • 本页导读 (1)
  • 背景信息
  • 前提条件
  • 注意事项
  • 客户端接入说明
  • 1. 安装依赖
  • 2. 配置MEK连接数据库
  • 3. 查询加密列的明文数据
  • 完整代码示例

点击开启售前

在线咨询服务