通过加密服务,可对应用服务的数据进行可靠的加解密运算,实现数据保护,同时满足数据安全方面的监管合规要求。本文介绍如何通过加密服务实现应用服务数据的加解密。
背景信息
部署方案
密码机提供密钥生成和管理、数据加密和解密功能,整个密码算法运算过程都在云密码机中完成,将密文数据存储在数据库中,提高了系统的安全性。下图为在应用系统中实现敏感数据加密和解密的典型部署方案。
加解密流程
敏感数据加密的时序图如下图所示。
数据加密流程
通过加密服务产生加密密钥。
使用加密密钥对应用服务数据的明文进行加密,产生应用数据密文,并将应用数据密文返回给应用服务。
应用服务将密文数据存储到数据库。
数据解密流程
应用系统从数据库读取已加密的密文数据,并将应用数据密文透传给云密码机进行解密。
密文解密后云密码机将明文信息返回给应用系统。
资源准备
开始配置前,需准备以下资源:
数据库:用于对应用数据密文进行安全存储和读取。
应用服务器:用于部署业务系统,支持以下系统:
64 位 Linux 系统
32 位或 64 位 Windows 系统
EVSM:TASS EVSM,用于提供加密和解密等密码服务。
步骤一:配置密码机客户端
如果业务需要正式上线,在购买加密服务实例后,需配置密码机客户端。
-
登录加密服务管理控制台,在顶部菜单栏,选择目标地域。
下载并安装密码机实例管理工具。
在虚拟密码机实例页面,找到目标密码机实例。
单击规格列的金融数据密码机,然后单击下载密码机实例管理工具。
解压获取到的密码机实例管理工具,选择以下任一方式安装。
在本地终端安装密码机客户端管理工具:安装后需通过 VPN 或物理专线使本地终端连接到密码机实例所属的 VPC 网络。
在 Windows 操作系统的 ECS 实例上安装密码机客户端管理工具:安装后需通过本地终端远程登录 ECS 实例,在 ECS 实例上操作密码机实例管理工具。购买 ECS 实例的具体操作,请参见自定义购买实例。
使用密码机实例管理工具初始化密码机实例。
登录密码机实例管理工具。具体操作,请参见密码机实例管理工具用户使用手册的《登录》章节。
初始化密码机实例。具体操作,请参见密码机实例管理工具用户使用手册的《出厂初始化》或《快速初始化》章节。
步骤二:初始化 SDK
使用以下方式初始化 SDK。
hsm=hsmGeneralFinance.getInstance("./cacipher.ini");步骤三:调用 Java 接口生成密钥
也可通过密码机管理工具产生随机密钥,具体操作请参见密码机管理工具用户使用手册的《对称密钥管理》章节。
调用以下 Java 接口生成工作密钥。
hsm.genWorkKey(keyType,algFlag,keyIndex,keyLable);接口定义
public String[] genWorkKey(String keyType,
char keyFlag,
int storeKeyIndex,
String storeKeyLabel)
throws cn.tass.exceptions.TAException请求参数
参数名称 | 参数类型 | 参数描述 |
keyType | String | 密钥类型。支持密钥类型编码和密钥类型名称两种格式。例如:ZEK/DEK 可传“00A”或“ZEK/DEK”两种格式。取值:
|
algFlag | char | 在 LMK 下加密的密钥密文标识。取值:
|
KeyIndex | int | 密钥存储索引。取值范围:1~2048。 |
KeyLabel | String | 密钥存储标签。包含 0~16 个 ASCII 字符,用于在云密码机内部存储密钥时标记密钥。 |
请求示例
keyType:00A
algFlag:R
keyIndex:1
KeyLabel:test返回值
0 号索引:密钥在 LMK 下加密的密文。
1 号索引:密钥校验值。
抛出异常
cn.tass.exceptions.TAException // 接口自定义异常。步骤四:配置 Java 接口
支持通过文件形式或内容形式配置 Java 接口。
文件形式
支持直接将配置文件绝对路径传入初始化接口内。
示例:
[LOGGER] logsw=error logPath=./ [HOST1] hsmModel=SJJ1310 linkNum=-5 host=192.168.19.19 port=8018 timeout=5 ifHeart=yes文件格式要求:
分类
要求
示例
注释
注释行以符号“#”起始,不支持行内注释。
#内容形式中的属性字段与文件形式中的属性字段保持一致。
配置域
配置域以方括号“[”和“]”标识。配置域与键名不区分大小写,为了便于区分建议配置域使用大写。
[LOGGER]
配置项
配置项格式:“键名(Key)=键值(Value)”。
linkNum=-5
配置内容
支持使用空白字符(空格或制表符)对内容进行对齐操作。
可在接口内拼装字符串传递配置。
使用“{”和“}”,表示包括所有内容;使用“;”,表示换行。
无
属性配置:Java 接口中配置文件的基本属性配置域包括日志属性、EVSM 属性和应用属性等,日志属性和EVSM 属性的属性字段说明请参见下表。
属性分类
配置域
属性字段
属性字段说明
日志属性
[LOGGER]
logsw
设置日志类别的开关,每种日志通过独立的关键字开启。取值:
error:错误日志
debug:调试日志
logPath
设置日志文件的保存目录。需确保该目录已经存在,且应用系统具有写入权限。
EVSM 属性
[HOSTn]:n 为该 EVSM 在当前配置文件中从 1 开始的序号,Java 接口会按顺序读取多个 EVSM 的属性,直到最后 1 个序号。
hsmModel
VSM 类型标识,用于指定 EVSM 驱动。默认值 SJJ1310。
linkNum
与云密码机建立长连接的数量。默认值 -10。数字前带负号(-)表示仅使用连接池,不带负号表示优先使用连接池;优先使用连接池时,如果业务并发过高,Java 接口中可能会创建短连接处理业务,对系统资源造成较大影响。
host
EVSM 主机服务 IP 地址,支持设置为域名形式。
port
EVSM 主机服务端口。
timeout
超时时间。单位为秒,默认值 6 秒。
connTimeout
指定 Java 接口与逃生服务器建立网络连接的超时时间。该属性字段不存在时,使用 timeout 的取值。
readTimeout
指定 Java 接口从逃生服务器读取信息的超时时间。该属性字段不存在时,使用 timeout 的取值。
socketProtocol
通讯协议,支持 TLSv1.2 等版本协议。默认值 TCP。
ifHeart
SDK 对服务端的心跳探测开关,默认开启。
内容形式
支持直接将配置信息以字符串的形式传入初始化接口内。示例:
// 内容形式中的属性字段与文件形式中的属性字段保持一致。
String config=
"{"
+"[LOGGER];"
+"logsw=error;logPath=./;"
+"[HOST1];"
+"hsmModel=SJJ1310;"
+"host=192.168.19.19;"
+"port=8018;"
+"connTimeout=5;"
+"}";步骤五:调用 Java 接口进行数据加解密
本文介绍如何通过调用 Java 接口生成工作密钥并进行通用数据加解密。
接口说明
数据加密
接口定义
调用以下 Java 接口进行数据加密。
hsm.generalDataEnc(algType,keyType,sm4SymmKey,disperFactor,sessionType,sessionFactor,padFlag,inData,IV);public byte[] generalDataEnc(int algType,
String keyType,
Object key,
String disperFactor,
int sessionType,
String sessionFactor,
int padFlag,
byte[] inData,
String IV)
throws cn.tass.exceptions.TAException请求参数
参数名称 | 参数类型 | 参数描述 |
algType | int | 加密算法模式。取值:
|
keyType | String | 加密数据的源密钥类型,支持密钥类型名称和密钥类型编码两种格式。例如:ZEK/DEK 可传“00A”或“ZEK/DEK”两种格式。取值:
|
key | Object | 加密数据密钥的索引或密文。
|
disperFactor | String | 密钥分散因子由 n 级分散因子串联组成,且每级分散因子必须为 16 字节。针对敏感数据加密场景,该参数取值为空字符串或 NULL。 |
sessionType | int | 会话密钥产生模式。针对敏感数据加密场景,该参数取值为空字符串或 NULL。
|
sessionFactor | String | 会话密钥因子。针对敏感数据加密场景,该参数取值为空字符串或 NULL。
|
padFlag | int | PAD 填充标识。针对敏感数据加密场景,该参数取值为 1。取值:
|
inData | byte[] | 输入的明文数据。 |
IV | String | 初始向量。仅当 algType 取值为 1、2、3、4 时支持该参数。ECB 加密算法模式不需要 IV,该参数取值为空字符串或 NULL。
|
请求示例
algType:1
keyType:"00A"
key:symmKey[0] // 加密的对称密钥密文,使用 EVSM 内部的索引密钥,使用内部密钥时为 int 类型。
disperFactor:null
sessionType:0
sessionFactor:null
padFlag:1
inData:根据应用业务自行输入数据内容。
IV:"00000000000000000000000000000000"返回值
加密之后的密文数据。
异常
cn.tass.exceptions.TAException // 接口自定义异常。 数据解密
接口定义
调用以下 Java 接口进行数据解密。
hsm.generalDataDec(algType,keyType,sm4SymmKey,disperFactor,sessionType,sessionFactor,padFlag,symmEnc,IV);public byte[] generalDataDec(int algType,
String keyType,
Object key,
String disperFactor,
int sessionType,
String sessionFactor,
int padFlag,
byte[] inData,
String IV)
throws cn.tass.exceptions.TAException请求参数
参数名称 | 参数类型 | 参数描述 |
algType | int | 加密算法模式。取值:
|
keyType | String | 加密数据的源密钥类型,支持密钥类型名称和密钥类型编码两种格式。例如:ZEK/DEK 可传“00A”或“ZEK/DEK”两种格式。取值:
|
key | Object | 加密数据密钥的索引或密文。
|
disperFactor | String | 密钥分散因子由 n 级分散因子串联组成,且每级分散因子必须为 16 字节。针对敏感数据加密场景,该参数取值为空字符串或 NULL。 |
sessionType | int | 会话密钥产生模式。针对敏感数据加密场景,该参数取值为空字符串或 NULL。
|
sessionFactor | String | 会话密钥因子。针对敏感数据加密场景,该参数取值为空字符串或 NULL。
|
padFlag | int | PAD 填充标识。针对敏感数据加密场景,该参数取值为 1。取值:
|
inData | byte[] | 输入的明文数据。 |
IV | String | 初始向量。仅当 algType 取值为 1、2、3、4 时支持该参数。ECB 加密算法模式不需要 IV,该参数取值为空字符串或 NULL。
|
请求示例
algType:1
keyType:"00A"
key:symmKey[0] // 加密的对称密钥密文,使用 EVSM 内部的索引密钥,使用内部密钥时为 int 类型。
disperFactor:null
sessionType:0
sessionFactor:null
padFlag:1
inData:自行输入加密后的数据。
IV:"00000000000000000000000000000000"返回值
解密后的数据。
抛出异常
cn.tass.exceptions.TAException // 接口自定义异常。代码示例
public class SensitiveDataEnc {
public static void main(String[] args) throws TAException {
// 接口初始化,采用配置文件的方式。
hsmGeneralFinance hsm = hsmGeneralFinance.getInstance("./cacipher.ini");
// 接口初始化 2,采用配置内容的方式。
// String config =
// "{"
// + "[LOGGER];"
// + "logsw=error;logPath=./;"
// + "[HOST1];"
// + "hsmModel=SJJ1310;"
// + "host=192.168.19.19;"
// + "port=8018;"
// + "connTimeout=5;"
// + "}";
//
// hsmGeneralFinance hsm = hsmGeneralFinance.getInstance(config);
// 测试 1 产生随机密钥 keyIndex 为可变参数。
// 当该密钥索引值取值为 0 时,表示不需要加密机保存生成的该条随机对称密钥。
// 当 keyIndex 取值为 1~2048 时,表示将对称密钥存储在加密机中,且相同索引会执行覆盖操作。
int keyIndex = 0;
switch (keyIndex) {
case 0:
// 产生随机 SM4 算法对称密钥。
String keyType = "00A";
char algFlag = 'R';
String keyLable = "sm4Key";
// 调用产生随机密钥接口。
String[] symmKey = hsm.genWorkKey(keyType, algFlag, keyIndex, keyLable);
System.out.println("对称密钥lmk下的密文值:" + symmKey[0]);
System.out.println("对称密钥校验值:" + symmKey[1]);
// 测试 2。
String str = "要加密的数据";
// CBC 模式。
int algType = 1;
// 密钥类型固定。
keyType = "00A";
// 执行加密的对称密钥密文。
String sm4SymmKey = symmKey[0];
// 也可以使用加密机内部的索引密钥,使用内部密钥时为 int 类型。
String disperFactor = null;
int sessionType = 0;
String sessionFactor = null;
// 遵循强制 80 填充。
int padFlag = 1;
byte[] inData = str.getBytes();
String IV = "00000000000000000000000000000000";
// 调用数据加密接口。
byte[] symmEnc = hsm.generalDataEnc(algType, keyType, sm4SymmKey, disperFactor, sessionType, sessionFactor,
padFlag, inData, IV);
System.out.println("16进制字符串输出对称加密结果:" + Forms.byteToHexString(symmEnc)
+ ",如果进行解密,可使用接口功能函数'Forms.hexStringToByte()'将16进制字符串转换为byte[]参与解密。");
// 调用数据解密接口。
byte[] symmDec = hsm.generalDataDec(algType, keyType, sm4SymmKey, disperFactor, sessionType, sessionFactor,
padFlag, symmEnc, IV);
System.out.println("解密结果与加密数据比较结果:" + Arrays.equals(symmDec, inData));
System.out.println("还原解密结果,通过字符集还原原文:" + new String(symmDec));
break;
// 此时生成对称密钥到 1 号索引位置,若已经存在,会执行覆盖操作。
case 1:
str = "要加密的数据";
// CBC 模式。
algType = 1;
// 密钥类型固定。
keyType = "00A";
// 执行加密的对称密钥密文,使用加密机内部的索引密钥,使用内部密钥时为 int 类型。
int sm4SymmKeyIndex = 1;
disperFactor = null;
sessionType = 0;
sessionFactor = null;
// 遵循强制 80 填充。
padFlag = 1;
inData = str.getBytes();
IV = "00000000000000000000000000000000";
// 调用数据加密接口。
symmEnc = hsm.generalDataEnc(algType, keyType, sm4SymmKeyIndex, disperFactor, sessionType, sessionFactor,
padFlag, inData, IV);
System.out.println("16进制字符串输出对称加密结果:" + Forms.byteToHexString(symmEnc)
+ ",如果进行解密,可使用接口功能函数'Forms.hexStringToByte()'将16进制字符串转换为byte[]参与解密。");
// 调用数据解密接口。
symmDec = hsm.generalDataDec(algType, keyType, sm4SymmKeyIndex, disperFactor, sessionType, sessionFactor,
padFlag, symmEnc, IV);
System.out.println("解密结果与加密数据比较结果:" + Arrays.equals(symmDec, inData));
System.out.println("还原解密结果,通过字符集还原原文:" + new String(symmDec));
break;
default:
break;
}
}
}步骤六:同步应用系统密钥
在部署密码机实例时,需同步应用系统的密钥和配置 Java 接口。应用密钥存储在密码机实例内部与外部系统时,同步方式不同。
密钥存储在密码机实例内部系统
需根据密钥索引将应用密钥备份导出,然后通过密钥恢复导入功能将备份密钥导入到其他密码机实例中,完成密钥同步操作。以下内容以从 A 密码机实例导出应用密钥,并导入到 B 密码机实例为例。
应用密钥由随机产生的备份密钥加密,可将加密后的密文以文件的形式导出或存储在 UKEY 中。同步密钥时,需将 UKEY 插入需要同步的设备中同步密钥。
备份导出 A 密码机实例的应用密钥。
打开密码机实例管理工具,通过 A 密码机实例的 IP 地址登录 VSM 管理系统。具体操作,请参见密码机实例管理工具用户使用手册的《登录》章节。
在顶部菜单栏,单击密钥管理页签,然后单击备份导出。
在选择导出密钥类型和索引对话框,根据实际需要选择密钥类型并输入密钥索引,然后单击确认。
说明可以选择将密钥备份保存到文件或保存到 UKEY 内。本文以将密钥备份保存到文件为例。
按照系统提示依次插入 3 个空 UKEY 并输入口令,单击下一步。
密码机实例将依次制作 3 个密钥备份密钥 KBK UKEY,建议 3 个 KBK UKEY 由 3 个密钥管理员分别保管。
选择要保存密钥密文的文件,EVSM 将导出全部应用密钥并保存到选择的文件中。单击下一步。
说明密钥备份完成后,需妥善保管 3 个 KBK UKEY 和密钥备份文件,待密钥恢复时使用。
将导出的应用密钥导入到 B 密码机实例进行恢复。
打开密码机实例管理工具,通过 B 密码机实例的 IP 地址登录 VSM 管理系统。
在顶部菜单栏,单击密钥管理页签,然后选择恢复导入 > 从文件中恢复密钥。
说明恢复密钥时,使用任意 2 个密钥备份 UKEY 即可还原出原始的 KBK 文件。
按照系统提示依次插入任意 2 个 KBK UKEY 并输入口令,单击下一步。
选择要读取的密钥备份文件,等待系统完成应用密钥的恢复,单击完成。
密钥存储在密码机实例外部系统
当应用密钥经过本地主密钥 LMK(Local Master Key)分组加密保护后存储在外部系统时,如需同步应用系统密钥,还需同步密码机实例的域名主密钥 DMK(Domain Master Key),可通过密码机实例的原始初始化产生 DMK 成分的 UKEY。
当多个密码机实例进行密钥备份时,只需在第一个密码机实例上完成原始初始化,然后对其他密码机实例恢复初始化,即可完成多个密码机实例的密钥同步。以下内容以在 A 密码机实例进行原始初始化,并在 B 密码机实例恢复初始化为例。
原始初始化将清空密码机实例内的所有密钥,如需保存密码机中的数据,请使用 UKEY 备份。备份的具体操作,请参见密码机实例管理工具的用户使用手册。
在 A 密码机实例上进行原始初始化:具体操作,请参见密码机实例管理工具用户使用手册的《原始初始化》章节。
打开密码机实例管理工具,通过 A 密码机实例的 IP 地址登录 VSM 管理系统。
在顶部菜单栏,单击密钥管理页签,然后单击原始初始化。
在弹出的警示提示框,单击下一步。
根据系统提示,制作至少 2 个域名主密钥 DMK 成分的 UKEY,建议采用3选2授权控制机制并制作 3 个授权 UKEY。
对 B 密码机实例进行恢复初始化(即导入 DMK)。
打开密码机实例管理工具,通过 B 密码机实例的 IP 地址登录 VSM 管理系统。
在顶部菜单栏,单击密钥管理页签,然后单击恢复初始化。
在弹出的警示提示框,单击下一步。
在恢复初始化-第一步对话框,输入 DMK 成分 UKEY 数目,单击下一步。
在恢复初始化-第二步对话框,依次插入 n 个成分 UKEY 并输入 UKEY 口令,单击导入成分 UKEY,密码机实例将读取 UKEY 内的 DMK 成分数据。
DMK 成分导入完成后,单击合成 DMK。
DMK 合成成功后,确定授权机制。
如果其他密码机实例共用一套授权 UKEY,请选择同步授权信息,只需插入有效授权的 UKEY 并输入口令,单击完成,完成恢复初始化。
如果每个密码机实例都需要使用独立的授权 UKEY,选中制作新的授权 UKEY,并在授权机制列表中选择1选1授权控制机制,制作授权 UKEY 完成恢复初始化。
在顶部菜单栏,单击密钥管理页签,然后单击获取 DMK 校验值。
说明当 DMK 同步到多个密码机实例时,可以通过比对多个密码机实例的 DMK 校验值来确定同步后的 DMK 是否一致。
在密钥管理页签,单击导出 DMK 成分。
可以将 DMK 成分导出到多个 UKEY 中,在原有密码机实例的成分 UKEY 丢失或损坏的情况下,能够重新合成出与原有密码机实例相同的 DMK 成分。
重要导出 DMK 成分时,不能保证 DMK 成分 UKEY 中的密钥备份与原有密码机实例的成分 UKEY 中的密钥备份完全相同。