# 调用示例与数据库改造示例

# 业务密钥的使用

业务密钥 调用kms服务端进行加解密, 比较方便

# 示例代码

//================================================
//KMSClientFactory
public static KMSClient createKMSClient() throws KMSException {
    String libFilepath;
    if (OSUtil.isWindows()) {
        libFilepath = "D:\\libSimkeySDFClient.dll";
    } else {
        libFilepath = "/mnt/linux/libSimkeySDFClient.so";
    }
    
    File lib = new File(libFilepath);
    System.out.println("lib:" + lib.getAbsolutePath());
    
    System.load(lib.getAbsolutePath());
    
    final String appId = "123456";//kms平台分配
    final String appSecret = "xxyyzz";//kms平台分配
    return new KMSClientImpl(appId, appSecret);
}
//================================================

KMSClient client = createKMSClient();

//应该只执行一次, 将得到的业务密钥id存入到数据库
KMSBizKey bizKey = client.genBizKey();

ClientRemoteEncryption remote = client.useBizkeyRemoteEncryption(bizKey.getEncKeyId(),
                                                                 bizKey.getHmacKeyId());
OutputFormat outputFormat = OutputFormat.SHORT;
//dbLine 指要加密的敏感数据
Map<String, String> dbLine = new HashMap<>();
dbLine.put("idCardNo", "441781201212128888");
dbLine.put("phoneNo", "18088888888");

//加密并计算sm3HMAC
Map<String, String> encMap = remote.encryptMapWithSm3hmac(dbLine, outputFormat);
//将密文保存到数据库
userinfoDao.update(encMap);
System.out.println("加密后:" + encMap);

//要显示时解密数据:
//encMap 此时应该是从数据库查询得到, userinfoDao.query()
Map<String, String> decMap = remote.decryptMapAndVerifySm3hmac(encMap);
System.out.println("解密后:" + decMap);

//简易解密:
ClientRemoteEasyDecryption easyDecryption = client.useClientRemoteEasyDecryption();
Map<String, String> easyDecMap = easyDecryption.decryptMapAndVerifySm3hmac(encMap);
System.out.println("简易解密后:" + easyDecMap);


//格式保留加密 fpe, 对手机号码的中间4位进行格式保留加密
String mobile = "18012344321";
List<SimkeyFpeAlphabet> simkeyFpeAlphabets = new ArrayList<>();
simkeyFpeAlphabets.add(SimkeyFpeAlphabet.Numeric);//数字类型的字符集
String fpePlain = mobile.substring(3, 7);
byte[] tweak = mobile.substring(7).getBytes(StandardCharsets.UTF_8);//最后4位不加密, 可以作为调整参数
String fpeEnc = remote.fpeEncrypt(simkeyFpeAlphabets, tweak, fpePlain);
System.out.println(fpePlain + " fpe加密结果:" + fpeEnc);//1234 fpe加密结果:3757
String mobileEnc = mobile.substring(0, 3) + fpeEnc + mobile.substring(7);
//执行结果 ==> 手机号码: 18012344321 fpe加密后重组为:18037574321
System.out.println("手机号码: " + mobile + " fpe加密后重组为:" + mobileEnc);

String fpeDec = remote.fpeDecrypt(simkeyFpeAlphabets, tweak, fpeEnc);
System.out.println(fpeEnc + " fpe解密结果:" + fpeDec);//3757 fpe解密结果:1234
//执行结果 ==> 手机号码: 18037574321 fpe解密后重组为:18012344321
System.out.println("手机号码: " + mobileEnc + " fpe解密后重组为:" + mobile.substring(0, 3) + fpeDec + mobile.substring(7));
Assertions.assertEquals(fpePlain, fpeDec);
//fpe demo 结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

# 数据库改造

  • 如果原文是BASE64格式的字符串,那么密文长度为 127+ 原文BASE64字符串的长度
  • 如果原文是UTF-8编码格式的字符串,那么密文长度为 127+ 原文UTF-8字符串长度 * 3 * 1.4
  • 如果要增加完整性校验,该表新增一列“sm3hmac”,用于保存sm3hmac的值, 长度为 300
  • 需要新增一张表或者原来的配置表 保存两把业务密钥的密钥id

# 用户密钥的使用

​ 如果不想将敏感数据传到kms服务端,可以使用用户密钥(数据密钥)进行本地加解密操作。

​ 可以为每个用户申请一组用户密钥(数据密钥),也可以所有用户只使用一组。

​ 用户密钥需要自己维护密钥关系, 比 业务密钥稍微复杂一点

# 示例代码

//================================================
//KMSClientFactory
public static KMSClient createKMSClient() throws KMSException {
    String libFilepath;
    if (OSUtil.isWindows()) {
        libFilepath = "D:\\libSimkeySDFClient.dll";
    } else {
        libFilepath = "/mnt/linux/libSimkeySDFClient.so";
    }
    
    File lib = new File(libFilepath);
    System.out.println("lib:" + lib.getAbsolutePath());
    
    System.load(lib.getAbsolutePath());
    
    final String appId = "123456";//kms平台分配
    final String appSecret = "xxyyzz";//kms平台分配
    return new KMSClientImpl(appId, appSecret);
}
//================================================

KMSClient client = createKMSClient();

//应该只执行一次, 将得到的业务密钥id存入到数据库
KMSBizKey bizKey = client.genBizKey();

//先生成用户密钥
ClientRemoteEncryption remote = client.useBizkeyRemoteEncryption(bizKey.getEncKeyId(), bizKey.getHmacKeyId());
KMSUserKey encUserkey = remote.genUserKey();
//用户密钥需要将下面4个值存入到数据库中
System.out.println("encUserkey.getEncKeyId:" + encUserkey.getEncKeyId());
System.out.println("encUserkey.getEncKey:" + encUserkey.getEncKey());
System.out.println("encUserkey.getHmacKeyId:" + encUserkey.getHmacKeyId());
System.out.println("encUserkey.getHmacKey:" + encUserkey.getHmacKey());

//使用用户密钥
ClientLocalEncryption localsoft = client.useUserkeyLocalSoftEncryption(encUserkey.getEncKeyId(),
                                                                       encUserkey.getEncKey(),
                                                                       encUserkey.getHmacKeyId(),
                                                                       encUserkey.getHmacKey());

OutputFormat outputFormat = OutputFormat.LONG;

//加密
byte[] data = "为天地立心,为生民立命,为往圣继绝学,为万世开太平".getBytes(StandardCharsets.UTF_8);
String encDatas = localsoft.encryptBytes2Str(data, outputFormat);
System.out.println("加密结果:" + encDatas);

//解密
byte[] decDatas = localsoft.decryptStr2Bytes(encDatas);
System.out.println("解密结果Hex:" + HexStringConvert.parseByte2HexStr(decDatas));
System.out.println("解密结果UTF8:" + new String(decDatas, StandardCharsets.UTF_8));

//从密文解析出keyid,然后去数据库 根据 keyid 找到对应的 密钥密文, 再调用 useUserkeyLocalSoftEncryption 方法
String keyidFromCiphertext = client.getKeyId(encDatas);
System.out.println("keyidFromCiphertext:" + keyidFromCiphertext);
System.out.println("encKeyid:" + encUserkey.getEncKeyId());

//hmac
String hmac = localsoft.sm3HMAC("可上九天揽月,可下五洋捉鳖,谈笑凯歌还".getBytes(StandardCharsets.UTF_8), outputFormat);
System.out.println("hmac:" + hmac);

//dbLine 指要加密的敏感数据, 对应数据库的一行
Map<String, String> dbLine = new HashMap<>();
dbLine.put("idCardNo", "441781201212123812");
dbLine.put("phoneNo", "18088888888");

//加密并计算sm3HMAC
Map<String, String> encMap = localsoft.encryptMapWithSm3hmac(dbLine, outputFormat);
//将密文保存到数据库
userinfoDao.update(encMap);
System.out.println("加密后:" + encMap);

//要显示时解密数据:
//encMap 此时应该是从数据库查询得到, userinfoDao.query()
Map<String, String> decMap = localsoft.decryptMapAndVerifySm3hmac(encMap);
System.out.println("解密后:" + decMap);

//测试 sm4 gcm 加解密 适用于数据加密传输
System.out.println("测试SM4 GCM加解密功能");
byte[] sm4gcmTestData = "1234567812345678123456781234567812345678123456781234567812".getBytes();
byte[] iv = HexStringConvert.parseHexStr2Byte("11223344556677881122334455667788");//16字节iv
byte[] aad = "simkey".getBytes();
String sm4gcmEnc = localsoft.sm4GcmEncrypt(sm4gcmTestData, iv, aad);
System.out.println("SM4 GCM 原文.Hex:" + HexStringConvert.parseByte2HexStr(sm4gcmTestData));
System.out.println("SM4 GCM 密文.BASE64:" + sm4gcmEnc);
System.out.println("SM4 GCM 密文.Hex:" + HexStringConvert.parseByte2HexStr(Base64NoCR.decode(sm4gcmEnc)));
byte[] sm4gcmDec = localsoft.sm4GcmDecrypt(sm4gcmEnc, iv, aad);
System.out.println("SM4 GCM 解密.Hex:" + HexStringConvert.parseByte2HexStr(sm4gcmDec));
Assertions.assertArrayEquals(sm4gcmTestData,sm4gcmDec,"SM4 GCM解密结果不正确");
System.out.println("SM4 GCM解密结果正确");

//还可以加解密文件
//加密文件
File srcFile = new File("D:\\KMS SDK接入规范.pdf");
File encFile = new File("D:\\KMS SDK接入规范.enc.pdf");
localsoft.encryptFile(srcFile, encFile);

//解密文件
File decFile = new File("D:\\KMS SDK接入规范.dec.pdf");
localsoft.decryptFile(encFile, decFile);

//简易解密
ClientLocalEasyDecryption easyDecryption = client.useClientRemoteEasyDecryption();
Map<String, String> easyDecMap = easyDecryption.decryptMapAndVerifySm3hmac(encMap);
System.out.println("简易解密后:" + easyDecMap);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

# 数据库改造

  • 如果原文是BASE64格式的字符串,那么密文长度为 300 + 原文BASE64字符串的长度
  • 如果原文是UTF-8编码格式的字符串,那么密文长度为 300 + 原文UTF-8字符串长度 * 3 * 1.4
  • 如果要增加完整性校验,该表新增一列“sm3hmac”,用于保存sm3hmac的值, 长度为 300
  • 需要新增一张表或者原来的配置表 保存两把业务密钥的密钥id
  • 如果为每个用户申请一组密钥,那么需要新建一张密钥表保存用户与密钥的对应关系
  • 如果只申请一组用户密钥则可以和业务密钥保存在同一个地方