集成EncJDBC

重要

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

若您希望使用Java应用程序访问数据库中被列加密保护的数据列,可以使用EncJDBC驱动程序连接数据库。此操作方便便捷,并能够降低使用全密态功能的成本。本文介绍如何通过EncJDBC访问数据库加密列的明文信息。

在持有用户密钥的情况下,数据传输链路全加密。您的客户端EncJDBC能够自动解密并返回明文数据,应用程序只需配置几行代码就可以展示明文数据。

前提条件

  • 已进行敏感识别的扫描任务,获得需要加密的敏感数据列信息。更多信息,请参见敏感识别

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

  • 已获取加密数据库连接信息。您首先需要获取加密数据库的连接信息,如域名(host)、端口(port)、数据库实例名(dbname)、用户名(username)、密码(password)等。

生成MEK

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

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

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

警告

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

可在数据库列加密配置中选择的加密方式,选择KMS密钥或生成本地密钥作为MEK来解密对应数据库。

KMS密钥

重要

使用KMS密钥时,请确保KMS服务可用。否则,全密态客户端驱动EncJDBC无法使用。

您需要获取数据库列加密配置中选择的KMS密钥所属KMS实例的访问地址,以及对应阿里云账号或RAM用户(必须具备KMS解密权限)的访问密钥AccessKeyIdAccessKeySecret,以便通过客户端读取该KMS密钥。具体操作如下:

  1. 使用阿里云账号RAM用户登录控制台。

  2. 如果是RAM用户,需要授予RAM用户KMS解密权限。

    1. 创建自定义权限策略。策略内容如下:

      {
          "Version": "1",
          "Statement": [
              {
                  "Effect": "Allow",
                  "Action": "KMS:Decrypt",
                  "Resource": "*"
              }
          ]
      }
    2. 将创建的自定义权限策略授予指定的RAM用户。具体操作,请参见RAM用户授权

  3. 获取KMS实例访问地址。

    • KMS实例中的密钥默认仅允许VPC网络访问,您可以在KMS的实例管理页面,找到目标KMS实例,单击操作列的详情,然后在实例信息页签,查看实例VPC地址。

      image

    • 如果需要通过公网访问密钥,需要在资源共享页签,开启公网访问后查看公网地址

      image

  4. 获取访问密钥。

    您需要在创建阿里云账号或RAM用户的AccessKey时,保存AccessKeyIdAccessKeySecret。具体操作,请参见创建AccessKey

本地密钥

数据库列加密配置中加密方式本地密钥时,您需要生成一个MEK。例如,00112233445566778899aabbccddeeff。

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

例如:

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

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

客户端接入说明

重要

Java使用JDK 1.8或以上版本。

在客户端侧将数据库连接驱动改为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不泄露。

根据数据库列加密配置中的加密方式,选择通过本地密钥或KMS密钥连接数据库。

通过KMS密钥连接数据库

重要
  • 如果使用STS临时访问凭证获取KMS托管的MEK,您可以使用STS SDK获取临时凭据STS Token。STS SDK示例,请参见STS SDK概览

  • 不能直接将访问密钥(AccessKeyIdAccessKeySecret)硬编码在业务代码中,本示例使用配置系统环境变量的方式管理访问密钥。具体内容,请参见Linux、macOSWindows系统配置环境变量

JDBC properties配置

标准的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";

// 从环境变量中获取访问密钥(AccessKey ID和AccessKey Secret)。
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// 如果使用STS临时访问凭证读取KMS密钥,还需要填写获取的STS安全令牌(SecurityToken)。
// String stsToken= "yourSecurityToken";

// KMS实例访问地址,开启公网访问使用公网地址。在VPC网络访问,使用实例VPC地址。
String kmsEndpoint = "kms.cn-hangzhou.aliyuncs.com";

Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("ALIBABA_CLOUD_ACCESS_KEY_ID", accessKeyId);
props.setProperty("ALIBABA_CLOUD_ACCESS_KEY_SECRET", accessKeySecret);
props.setProperty("ALIBABA_CLOUD_KMS_ENDPOINT", kmsEndpoint);
// props.setProperty("ALIBABA_CLOUD_STS_TOKEN","stsToken");



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

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

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

// ... 发起查询 ...
URL配置

支持通过URL链接中嵌入获取KMS密钥的参数。如下面所示:

// 准备好连接地址(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";

// 从环境变量中获取访问密钥(AccessKey ID和AccessKey Secret)。
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// 如果使用STS临时访问凭证读取KMS密钥,还需要填写获取的STS安全令牌(SecurityToken)。
// String stsToken= "yourSecurityToken";

// KMS实例访问地址,开启公网访问使用公网地址。在VPC网络访问,使用实例VPC地址。
String kmsEndpoint = "kms.cn-hangzhou.aliyuncs.com";

// 下面是MySQL版数据库的连接URL格式。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s?ALIBABA_CLOUD_ACCESS_KEY_ID=%s&ALIBABA_CLOUD_ACCESS_KEY_SECRET=%s&ALIBABA_CLOUD_KMS_ENDPOINT=%s", hostname, port, dbname, accessKeyId,accessKeySecret,kmsEndpoint);
// 使用STS Token。
// String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s?ALIBABA_CLOUD_ACCESS_KEY_ID=%s&ALIBABA_CLOUD_ACCESS_KEY_SECRET=%s&ALIBABA_CLOUD_KMS_ENDPOINT=%s&ALIBABA_CLOUD_STS_TOKEN=%s", hostname, port, dbname, accessKeyId,accessKeySecret,kmsEndpoint,stsToken);

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

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

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

通过本地密钥连接数据库

JDBC properties配置

标准的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等参数。

说明

文件配置方式仅适用配置本地密钥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配置

支持通过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");
}

常见问题

  • Q:运行程序时报错Exception in thread "main" java.lang.IllegalAccessError: class com.alibaba.encdb.common.SymCrypto (in unnamed module @0x5c0369c4) cannot access class com.sun.crypto.provider.SunJCE (in module java.base) because module java.base does not export com.sun.crypto.provider to unnamed module @0x5c0369c4,如何处理?

    A: 该报错可能是因为您的JDK版本较高导致的模块间权限问题,请在运行时添加VM option参数--add-exports=java.base/com.sun.crypto.provider=ALL-UNNAMEDcom.sun.crypto.provider导出给Unnamed模块,以解决访问权限问题。

  • Q:运行程序时报错failed in mek provision: you might have an incorrect mek setting. Detail:gcmEncrypt error,如何处理?

    A:该问题常见于Oracle系列的JDK,如需解决此问题,您可以任选如下两种方式之一:

    • 使用Amazon Correto系列的JDK。

    • 仍然使用Oracle系列的JDK,但需要手动配置Security provider。具体步骤如下:

      1. 找到JDK的安装路径。

      2. 安装路径/conf/security/路径下,找到java.security文件。

      3. 编辑java.security文件,在List of providers and their preference orders (see above):区域,补充如下内容:

        security.provider.14=org.bouncycastle.jce.provider.BouncyCastleProvider