暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

GaussDB使用JDBC操作密态数据库

高斯精选 2023-07-18
404

获取JDBC驱动包
获取JDBC驱动包
密态数据库支持的JDBC驱动包为gsjdbc4.jar、opengaussjdbc.jar、gscejdbc.jar。

gsjdbc4.jar: 主类名为“org.postgresql.Driver”,数据库连接的url前缀为“jdbc:postgresql”。
opengaussjdbc.jar:主类名为“com.huawei.opengauss.jdbc.Driver”,数据库连接的url前缀为“jdbc:opengauss”。
gscejdbc.jar(目前只在部分操作系统下支持):主类名为“com.huawei.gaussdb.jdbc.Driver”,数据库连接的url前缀为“jdbc:gaussdb”,密态场景推荐使用此驱动包。如果驱动包中不包含gscejdbc.jar,则也可以使用opengaussjdbc.jar或gsjdbc4.jar包。
配置LD_LIBRARY_PATH
密态场景使用JDBC驱动包时,需要先设置环境变量LD_LIBRARY_PATH。

使用gscejdbc.jar驱动包时,gscejdbc.jar驱动包中密态数据库需要的依赖库会自动拷贝到该路径下,并在开启密态功能连接数据库的时候加载。
使用opengaussjdbc.jar或gsjdbc4.jar时,需要同时解压包名为GaussDB-Kernel_数据库版本号_操作系统版本号_64bit_libpq.tar.gz的压缩包解压到指定目录,并将lib文件夹所在目录路径,添加至LD_LIBRARY_PATH环境变量中。
注意:
全密态场景使用JDBC驱动包时需要有System.loadLibrary权限,以及环境变量LD_LIBRARY_PATH中第一优先路径的文件读写权限,建议使用独立目录作为全密态依赖库的存放路径。若在执行的时候指定java.library.path,需要与LD_LIBRARY_PATH的第一优先路径保持一致。

使用gscejdbc.jar时,jvm加载class文件需要依赖系统的libstdc库,若开启密态则gscejdbc.jar会自动拷贝密态数据库依赖的动态库(包括libstdc库)到用户设定的LD_LIBRARY_PATH路径下。如果依赖库与现有系统库版本不匹配,则首次运行仅部署依赖库,再次调用后即可正常使用。

执行SQL语句
在执行本节的SQL语句之前,请确保已完成前两阶段:准备阶段、配置阶段。

本节以完整的执行流程为例,介绍如何使用密态数据库语法,包括三个阶段:使用DDL阶段、使用DML阶段、清理阶段。

JDBC开发中与非密态场景操作一致的部分请参考《开发者指南》中“应用程序开发教程 > 基于JDBC开发”章节。

密态数据库连接参数
enable_ce:String类型。其中enable_ce=0表示不开启全密态开关,enable_ce=1表示支持密态等值查询基本能力,enable_ce=2表示在密态等值查询能力上支持客户端排序(实验室特性),enable_ce=3表示在密态等值查询能力的基础上支持软硬融合(当前特性是实验室特性,使用时请联系华为工程师提供技术支持)。


// 以下用例以gscejdbc.jar驱动为例,如果使用其他驱动包,仅需修改驱动类名和数据库连接的url前缀。
// gsjdbc4.jar: 主类名为“org.postgresql.Driver”,数据库连接的url前缀为“jdbc:postgresql”。
// opengaussjdbc.jar:主类名为“com.huawei.opengauss.jdbc.Driver”,数据库连接的url前缀为“jdbc:opengauss”。
// gscejdbc.jar:主类名为“com.huawei.gaussdb.jdbc.Driver”,数据库连接的url前缀为“jdbc:gaussdb”

public static void main(String[] args) {
    // 驱动类。
    String driver = "com.huawei.gaussdb.jdbc.Driver";
    // 数据库连接描述符。enable_ce=1表示支持密态等值查询基本能力。
    String sourceURL = "jdbc:gaussdb://10.10.0.13:8000/postgres?enable_ce=1";
    String username = "admin";
    String passwd = "Gauss_234";
    Connection conn = null;
    try {
        // 加载驱动
        Class.forName(driver);
        // 创建连接
        conn = DriverManager.getConnection(sourceURL, username, passwd);
        System.out.println("Connection succeed!");
        // 创建语句对象
        Statement stmt = conn.createStatement();

        // 创建客户端主密钥
        // 此处例举了多种场景下创建主密钥的方式,请根据实际场景选择其中一种方式即可:gs_ktool密钥管理工具,华为云密钥服务(即huawei_kms)
        // KEY_PATH格式请参考:《开发者指南》中“SQL参考 > SQL语法 > CREATE CLIENT MASTER KEY”章节 
        int rc = stmt.executeUpdate("CREATE CLIENT MASTER KEY ImgCMK1 WITH ( KEY_STORE = gs_ktool , KEY_PATH = \"gs_ktool/1\" , ALGORITHM = AES_256_CBC);");
        // 华为云场景下,KEY_PATH中需使用项目ID与密钥ID,在准备阶段已介绍如何获取密钥ID,配置阶段已介绍如何获取项目ID
        // int rc = stmt.executeUpdate("CREATE CLIENT MASTER KEY ImgCMK1 WITH ( KEY_STORE = huawei_kms , KEY_PATH = 'https://kms.cn-north-4.myhuaweicloud.com/v1.0/00000000000000000000000000000000/kms/00000000-0000-0000-0000-00000000000', ALGORITHM = AES_256);");
        // 创建列加密密钥
        int rc2 = stmt.executeUpdate("CREATE COLUMN ENCRYPTION KEY ImgCEK1 WITH VALUES (CLIENT_MASTER_KEY = ImgCMK1, ALGORITHM  = AES_256_GCM);");
        // 创建加密表
        int rc3 = stmt.executeUpdate("CREATE TABLE creditcard_info (id_number int, name varchar(50) encrypted with (column_encryption_key = ImgCEK1, encryption_type = DETERMINISTIC),credit_card varchar(19) encrypted with (column_encryption_key = ImgCEK1, encryption_type = DETERMINISTIC));");
        // 插入数据
        int rc4 = stmt.executeUpdate("INSERT INTO creditcard_info VALUES (1,'joe','6217986500001288393');");
        // 查询加密表
        ResultSet rs = null;
        rs = stmt.executeQuery("select * from creditcard_info where name = 'joe';");
        // 删除加密表
        int rc5 = stmt.executeUpdate("DROP TABLE IF EXISTS creditcard_info;");
        // 删除列加密密钥
        int rc6 = stmt.executeUpdate("DROP COLUMN ENCRYPTION KEY IF EXISTS ImgCEK1;");
        // 删除客户端主密钥
        int rc7 = stmt.executeUpdate("DROP CLIENT MASTER KEY IF EXISTS ImgCMK1;");
        // 关闭语句对象
        stmt.close();
        // 关闭连接
        conn.close();
    } catch (Exception e) {
        e.printStackTrace();
        return;
    }
}


调用isValid方法刷新缓存示例

Connection conn1 = DriverManager.getConnection("url","user","password");
// 在另外一个连接conn2中创建客户端主密钥
...
// conn1通过调用isValid刷新缓存
try {
	if (!conn1.isValid(60)) {
		System.out.println("isValid Failed for connection 1");
	}
} catch (SQLException e) {
	e.printStackTrace();
        return null;
}


image.png

说明:
下列场景可以解密成功,但不推荐:

密文长度入参可以比实际密文长。
schema.table.column指向其他加密列。此时将返回被指向的加密列的原始数据类型。

image.png

import org.postgresql.jdbc.PgConnection;
import org.postgresql.jdbc.clientlogic.ClientLogicDecryptResult;

// conn为密态连接
// 调用密态PgConnection的decryptData方法对密文进行解密,通过列名称定位到该密文的所属加密列,并返回其原始数据类型
ClientLogicDecryptResult decrypt_res = null;
decrypt_res = ((PgConnection)conn).decryptData(ciphertext, ciphertext.length(), schemaname_str,
        tablename_str, colname_str);
// 检查返回结果类解密成功与否,失败可获取报错信息,成功可获得明文及长度和原始数据类型
if (decrypt_res.isFailed()) {
    System.out.println(String.format("%s\n", decrypt_res.getErrMsg()));
} else {
    System.out.println(String.format("decrypted plaintext: %s size: %d type: %s\n", decrypt_res.getPlaintext(),
        decrypt_res.getPlaintextSize(), decrypt_res.getOriginalType()));
}


执行加密表的预编译SQL语句

PreparedStatement pstmt = conn.prepareStatement("INSERT INTO creditcard_info VALUES (?, ?, ?);");
// 调用PreparedStatement的setShort设置参数。
pstmt.setInt(1, 2);
pstmt.setString(2, "joy");
pstmt.setString(3, "6219985678349800033");
// 调用PreparedStatement的executeUpdate方法执行预编译SQL语句。
int rowcount = pstmt.executeUpdate();
// 调用PreparedStatement的close方法关闭预编译语句对象。
pstmt.close();


执行加密表的批处理操作

Connection conn = DriverManager.getConnection("url","user","password");
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO batch_table (id, name, address) VALUES (?,?,?)");
// 针对每条数据都要调用setShort设置参数,以及调用addBatch确认该条设置完毕。
int loopCount = 20;
 for (int i = 1; i < loopCount + 1; ++i) {
      statemnet.setInt(1, i);
      statemnet.setString(2, "Name " + i);
      statemnet.setString(3, "Address " + i);
      // Add row to the batch.
      statemnet.addBatch();
}
// 调用PreparedStatement的executeBatch方法执行批处理。
int[] rowcount = pstmt.executeBatch();
// 调用PreparedStatement的close方法关闭预编译语句对象。
pstmt.close();


「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论