本文介绍如何实现全加密云数据库功能。

前提条件

配置步骤

  1. RDS Postgres数据库添加插件
  2. 创建加密表
  3. 安装libencdb.jar包
  4. 添加依赖
  5. 用户侧数据加密
  6. 用户侧数据解密

步骤一:部署插件

全加密云数据库在RDS PostgreSQL服务端以插件方式部署,用户无需额外开通流程即可使用全部相应功能。您可以通过如下语句实现部署:

create extension encdb;
说明 数据库要求部署插件的用户有superuser权限。

步骤二:创建加密表

创建加密表时,您可以根据自身需要,将敏感字段的数据类型替换为对应的加密数据类型。以下表为例,其原始定义如下:

CREATE TABLE example (
  id     integer,
  name   varchar,
  price  integer,
  miles  float4,
  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)
)
说明
  • 全加密数据库功能目前支持det_type和rnd_type两种加密数据类型。其中det_type为确定性加密(同一个数字两次加密得到的密文相同),rnd_type为不确定性加密 (同一个数字两次加密得到的密文不同)。
  • 当您在应用程序中使用det_type和rnd_type时,需要将全加密数据库det_type和rnd_type对应的宿主变量转换为string数据类型。
  • 您不能直接修改原有表(即Alter table),而是需要新建一个加密表,然后再将原表数据通过SDK加密后导入。

步骤三:安装libencdb.jar包

如果您的Java项目使用Maven构建,可以通过如下命令将上面的libencdb.jar包安装至您的本地仓库:
mvn install:install-file -DgroupId=com.alibaba.encdb -DartifactId=<安装的Jar包名> -Dversion=<安装的Jar版本> -Dpackaging=jar -Dfile=libs/<安装的Jar包名.jar>
示例:
mvn install:install-file -DgroupId=com.alibaba.encdb -DartifactId=libencdb -Dversion=1.2.1 -Dpackaging=jar -Dfile=libs/libencdb-1.2.1.jar
说明 此示例是将libencdb-1.2.1.jar包下载至libs路径下。

步骤四:添加依赖

在自建项目中,可以使用libencdb配合原生JDBC与加密数据库进行交互。即应用程序在与数据库交互时,调用libencdb提供的加解密函数,对明文数据进行加密或者解密。

您需要在pom.xml中添加如下依赖:

    <dependency>
      <groupId>com.alibaba.encdb</groupId>
      <artifactId>libencdb</artifactId>
      <version>1.2.1</version>
    </dependency>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>42.2.10</version>
    </dependency>
    <dependency>
      <groupId>org.bouncycastle</groupId>
      <artifactId>bcpkix-jdk15on</artifactId>
      <version>1.62</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.62</version>
    </dependency>

    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>24.1.1-jre</version>
    </dependency>

步骤五:数据加密

为了保护用户数据,在业务代码中插入数据时需要进行加密。

  • 步骤一

    声明变量并创建Crypto实例,代码片段如下:

    Class.forName("org.postgresql.Driver");
    
    String hostname = "11.160.48.100";  //主机名称
    String port = "9994";               //端口号
    String dbname = "postgres";         //数据库名称
    String username = "encdb_test";     //用户名
    String password = "encdb_test";     //密码
    String tableName = "example";       //表名称
    byte[] mek = new byte[] {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (byte) 0x88, (byte) 0x99, (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 0xee, (byte) 0xff}; 
                                        //用户根密钥,长度为16字节。
    
    String dbUrl = "jdbc:postgresql://" + hostname + ":" + port + "/" + dbname+"?binaryTransfer=true";
    Connection dbConnection = DriverManager.getConnection(dbUrl, username, password);
    
    
    int algo = CryptoConstants.AES_128_CBC;  //加密算法,可设定加密算法为AES_128_GCM、AES_128_CTR、AES_128_CBC、SM4_128_CBC
    CryptoSDK crypto = CryptoManager.createCryptoNoTEE(username, mek, dbConnection, algo);
    说明 参数mek和algo只作为libencdb的加解密模块使用,不会直接传送给服务器端。
  • 步骤二

    插入数据并进行加密,示例代码如下:

    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();
    说明 示例代码在数据库postgres的example表中插入了一组数据 ,其中字段price、miles和secret为加密数据。
  • 步骤三

    连接数据库查看插入数据库中的数据,请输入如下命令:

    postgres=# select * from example;

    得到结果如下:

     id | name |                                  price                                   |                                  miles                                   |                                  secret
    ----+------+--------------------------------------------------------------------------+--------------------------------------------------------------------------+--------------------------------------------------------------------------
      1 | name | \xdf4901df087c6a3e0325175bb76942c684191a8dda2a8d0c35f295dc1e30eaeaa0c0e3 | \x315102ea5ab8a659066ab672e6dfbfd89a3a2b360bf6efe3787931e00f61af05f7408c | \xed4903dfd1bda1a89ad6aa7e8905c0e6305e15db4bc9ce2d2cfac9e25094d2a3ed367d
    (1 行记录)
    说明 已被加密的数据在服务器端无法查看,能够有效防御来自云平台外部和内部的安全威胁,时刻保护用户数据。

步骤六:数据解密

在业务代码中可对从数据库中获取的数据进行解密,代码片段如下:

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("price:" + price + "\n" + "miles:" + miles + "\n" + "text:" + text);

}
说明 上述代码执行后,在输出结果中可以查看到解密后的数据内容。