本文介绍如何在非Intel SGX 增强型实例中配置和使用全加密云数据库功能。
前提条件
- RDS PostgreSQL服务端
- 本地客户端
- 已具备Java 1.8+的开发环境。
- 已根据服务端版本下载对应EncDB依赖包。
- 已生成用户主密钥。
说明
- 用户主密钥是您访问加密数据的根凭据,全加密功能不提供用户主密钥的生成和备份服务,您需要自行生成用户主密钥。一旦您丢失密钥,将无法再访问已有的数据。因此我们建议您妥善备份用户主密钥。
- 用户主密钥由长度为16字节的16进制字符串组成,本示例以
0x00112233445566778899aabbccddeeff
为例。常见的生成方法有:密码生成工具(如openssl, openssl rand -hex 16
)、编程语言中的random函数。
背景信息
在RDS PostgreSQL的非
Intel SGX 安全增强型规格实例上,全加密功能可以利用密码学方法提供数据安全保护,但支持的操作类型较少,具体请参见
功能支持。
说明 RDS PostgreSQL 10或其以上版本的所有实例规格都支持
全加密功能,但推荐您选择
Intel SGX 安全增强型规格,以获得最完整的全加密功能支持。安全增强型规格的配置步骤,请参见
配置步骤(安全增强型实例)。
本文以非Intel SGX 安全增强型规格的PostgreSQL实例为例,介绍如何通过客户端连接并使用全加密云数据库。
服务端配置步骤
全加密云数据库在RDS PostgreSQL服务端以扩展(Extension)形式部署。本步骤介绍如何在服务器端安装扩展以及创建加密表。
- 安装EncDB扩展。
- 创建加密表。
创建加密表时,您可以根据自身需要,将敏感字段的数据类型替换为对应的加密数据类型。以下表为例,其原始定义如下:
CREATE TABLE example (
id INTEGER,
name VARCHAR,
price INTEGER,
miles REAL,
secret TEXT,
PRIMARY KEY (id)
);
如果选择price, miles, secret为敏感数据,创建的加密表定义如下:
CREATE TABLE example (
id INTEGER,
name VARCHAR,
price rnd_type,
miles det_type,
secret rnd_type,
PRIMARY KEY (id)
);
说明
- 非Intel SGX 增强型实例中,全加密数据库功能目前支持det_type和rnd_type两种加密数据类型。其中det_type为确定性加密(同一个敏感数据两次加密得到的密文相同),rnd_type为不确定性加密 (同一个敏感数据两次加密得到的密文不同)。
- 当您在应用程序中使用det_type和rnd_type时,需要将全加密数据库det_type和rnd_type对应的宿主变量转换为string数据类型。
- 您不能直接修改原有表(即
ALTER TABLE
),而是需要新建一个加密表,然后再将原表数据通过SDK加密后导入。
客户端配置步骤
本步骤介绍客户端如何连接数据库并进行数据加密解密操作。
- 安装EncDB依赖包。
mvn install:install-file -DgroupId=com.alibaba.encdb -DartifactId=<安装的Jar包名> -Dversion=<安装的Jar版本> -Dpackaging=jar -Dfile=<下载的Jar包所在路径>/<安装的Jar包名.jar>
示例:
mvn install:install-file -DgroupId=com.alibaba.encdb -DartifactId=libencdb -Dversion=1.2.4 -Dpackaging=jar -Dfile=D:\encdb\libs\libencdb-1.2.4.jar
说明 此示例中EncDB依赖包在D:\encdb\libs路径下。
- 添加依赖。
您需要在pom.xml中添加如下依赖:
<dependency>
<groupId>com.alibaba.encdb</groupId>
<artifactId>libencdb</artifactId>
<version>1.2.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.62</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.62</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.23</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>24.1.1-jre</version>
</dependency>
- 全加密功能使用示例。
- 连接数据库。
说明 此示例中MEK仅为测试用,实际应用中请使用自己的MEK。参数Mek和EncAlgo只作为EncDB SDK的加解密模块使用,不会直接传送给服务器端。
try {
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String hostname = "pgm-uf6qw1ap115gr2****.pg.rds.aliyuncs.com";
String port = "5432";
String dbname = "encdbtest";
String username = "username";
String password = "userpassword";
String tableName = "example";
String mek = "0x00112233445566778899aabbccddeeff";
String dbUrl = "jdbc:postgresql://" + hostname + ":" + port + "/" + dbname + "?binaryTransfer=true";
try {
dbConnection = DriverManager.getConnection(dbUrl, username, password);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
/*创建EncdbSDK实例*/
EncdbSDK sdk = EncdbSDKBuilder.newInstance()
.setDbConnection(dbConnection)
.setMek(mek)
.setEncAlgo(Constants.EncAlgo.SM4_128_CBC)
.setDekGenMode(Constants.DekGenMode.LOCAL)
.setSdkMode(Constants.SDKMode.Crypto)
.build();
Cryptor crypto = sdk.getCryptor();
上述示例中关键参数解释及取值示例如下:
参数 |
取值示例(字符串类型) |
说明 |
Mek |
0x00112233445566778899aabbccddeeff |
用户主密钥。
取值范围:长度为16字节的16进制字符串。
|
EncAlgo |
SM4_128_CBC |
加密算法。
取值范围:
- AES_128_GCM
- AES_128_CBC
- SM4_128_CBC(默认值)
|
DekGenMode |
LOCAL |
生成数据密钥的方式。固定为LOCAL,不能修改。
|
SdkMode |
Crypto |
SDK模式。固定为Crypto,不能修改。 |
- 插入加密数据。
PreparedStatement stmt = dbConnection.prepareStatement("insert into " + tableName + " (id, name, price, miles, secret) values(?,?,?,?,?)");
int price = 1234;
float miles = 12.34f;
String secret = "aliyun";
stmt.setInt(1, 1);
stmt.setString(2, "name");
stmt.setBytes(3, crypto.encrypt(tableName, "price", String.valueOf(price)));
stmt.setBytes(4, crypto.encrypt(tableName, "miles", String.valueOf(miles)));
stmt.setBytes(5, crypto.encrypt(tableName, "secret", secret));
stmt.execute();
- 查询解密数据。
String sqlCmd = "SELECT * from " + tableName + " where miles = ?";
PreparedStatement stmt = dbConnection.prepareStatement(sqlCmd);
stmt.setBytes(1, crypto.encrypt(tableName, "miles", "12.34"));
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString(2);
int price = Integer.parseInt(crypto.decryptString(rs.getBytes(3)));
float miles = Float.parseFloat(crypto.decryptString(rs.getBytes(4)));
String text = crypto.decryptString(rs.getBytes(5));
System.out.println(id + ", " + name + ", " + price + ", " + miles + ", " + text);
}
查询结果:
- 已加密的数据在客户端查询时被解密,您可以查询到明文。
1, name, 1234, 12.34, aliyun
- 已被加密的数据在服务器端无法查看,能够有效防御来自云平台外部和内部的安全威胁,时刻保护用户数据。
select * from example;
id | name | price | miles | secret
----+------+--------------------------------------------------------------------------+--------------------------------------------------------------------------+--------------------------------------------------------------------------
1 | name | \xdf4901df087c6a3e0325175bb76942c684191a8dda2a8d0c35f295dc1e30eaeaa0c0e3 | \x315102ea5ab8a659066ab672e6dfbfd89a3a2b360bf6efe3787931e00f61af05f7408c | \xed4903dfd1bda1a89ad6aa7e8905c0e6305e15db4bc9ce2d2cfac9e25094d2a3ed367d
(1 行记录)