本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。
通过加密服务,您能对应用服务的数据进行可靠的加解密运算,实现数据保护,同时满足数据安全方面的监管合规要求。本文介绍如何通过加密服务实现应用服务数据的加解密。
背景信息
下图为在应用系统中实现敏感数据加密和解密的典型部署方案。
数据加密流程
通过加密服务产生加密密钥。
使用加密密钥对应用服务数据的明文进行加密,产生应用数据密文,并将应用数据密文返回给应用服务。
应用服务把密文数据存储到数据库。
数据解密流程
应用系统从数据库读取已加密的密文数据,并将应用数据密文透传给云密码机进行解密。
密文解密后云密码机将明文信息返回给应用系统。
密码机提供密钥生成和管理、数据加密和解密功能。整个密码算法运算过程都在云密码机中完成,数据通过密码机处理后把密文数据存储在数据库中,提高了系统的安全性。下图为敏感数据加密的时序图。
资源准备
开始配置前,您需要准备以下资源:
数据库:用于对应用数据密文进行安全存储和读取。
应用服务器:用于部署用户的业务系统,支持如下系统。
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个ASII字符。 说明 在云密码机内部存储密钥时标记密钥的标签说明。 |
请求示例
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对服务端的心跳探测,默认开启。
内容形式:支持直接将配置信息以字符串的形式传入初始化接口内。示例:
//内容形式中的属性字段与文件形式中的属性字段保持一致。 Stringconfig= "{" +"[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密码机实例恢复初始化为例。
在A密码机实例上进行原始初始化。
警告原始初始化将清空密码机实例内的所有密钥,如果您希望保存密码机中的数据,请使用UKEY备份。备份的具体操作请参见密码机实例管理工具的用户使用手册。
打开密码机实例管理工具,通过A密码机实例的IP地址登录VSM管理系统。
具体操作,请参见密码机实例管理工具用户使用手册的《登录》章节。
在顶部菜单栏,单击密钥管理页签,然后单击原始初始化。
在弹出的警示提示框,单击下一步。
根据系统提示,制作至少2个域名主密钥DMK成份的UKEY。
建议您采用3选2授权控制机制并制作3个授权UKEY。
说明具体操作,请参见密码机实例管理工具用户管理手册的《原始初始化》章节。
对B密码机实例进行恢复初始化(即导入DMK)。
警告恢复初始化将清空密码机实例内的所有密钥,如果您希望保存密码机中的数据,请使用UKEY备份。备份的具体操作请参见密码机实例管理工具的用户使用手册。
打开密码机实例管理工具,通过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中的密钥备份完全相同。