操作指南

最近更新时间:2020-06-29 20:10:30

操作流程

API Key 是常用的一类用于客户端应用与后台服务器建立可信连接和数据通信的密钥。下面就以 API Key 为例,介绍腾讯云提供的密钥保护解决方案。

用户使用 API Key 的流程如下图所示:

操作流程说明如下:

  1. 管理员登录密钥管理系统 KMS 控制台创建白盒密钥。
  2. 管理员使用白盒密钥管理功能对待加密的文本(例如 API Key)进行加密,得到密文。
  3. 管理员将前两步中生成的解密密钥和 API Key 密文分发给各业务方的开发或运维人员。
  4. 业务开发/运维人员将白盒解密密钥(密文)以文件的形式部署到用户的业务服务器上。
  5. 业务开发人员将 API Key 密文和初始化向量作为业务系统的输入参数。
  6. 业务开发人员将白盒解密 SDK 集成到业务系统中,并使用步骤5中的参数来解密 API Key,在内存中动态的获得 API Key 的明文,用于各项业务的访问认证、鉴权等各项操作。

从上述流程可以看出,API Key 并没有暴露在开放环境中,在实际的用户业务的服务器上只保存了 API Key 的密文,以及需要的白盒解密密钥,白盒解密算法和密钥混淆后无需额外的明文密钥进行解密,这就确保了整个链路的安全性。针对 GitHub 上源代码的 API Key 泄露事件,如果采用白盒密钥管理的解决方案,就可以彻底杜绝 API Key 的明文暴露在源代码中,从而防止 API Key 被恶意利用。

操作步骤

步骤1:创建白盒密钥

创建白盒密钥对是通过调用白盒服务来实现的,支持控制台方式和 API 方式。

本文示例使用腾讯云 命令行工具 TCCLI ,后续您可以使用任何受支持的编程语言调用。 请求命令:

tccli kms CreateWhiteBoxKey --region ap-guangzhou --Alias test-gz01 --Description 'this is test for gz key' --Algorithm 'SM4'

返回结果示例:

{
    "Response": {
        "RequestId": "55f7ca05-17af-4bfb-87a5-80a80a4b0761",
        "EncryptKey": "BA4AADZhzTBr7vmCDyHwQRCKTSCXm8pnGs38mJfA1Pw+VaP8/MW+lSCTYIi/0AUsido39JWw2XkvrnmauXsU3cemHQ+bcpofelLB2nS2ShCINi9MTSS/KgAKhvQPD5jXtbbmUSiBcKLTMXxH6MZXcPyGvEmqe3SrhvqflQk4bL/uUxAW1IC4p7E07pn6RQr2zMZAdkK0IciuwV23+cp25x2G/fEGtZi46YZR48tb2ZciGQ5kzS8se/CJM+lTqdBwAHV6yvPVQaXS16NFp4UhAtGCRZVfMQCp56hkhEryGjtxvIz4qfgCZ4bEo3Xp2DnNtzwBmtRBnCXl2PbLABRfGZ9ZX7uN3IMv3rJ1mRJWJh2eBHR1N+ujO7bGTqYHIb9VxxafQ/D9PHhGO3LeQRrNi1LS31XClvVNqNR8p/gc4GQCs987q0vdXC2PfinEV0fkyAKzis6096Jo7gfK+QvjYgVNRPr0nVuKKMI60zDKHG5jnN5kRKuTYfhl2tppFQO2WpvCLPHQH3DcX2FZGAgmvV1yjMSp79dOgS3tnbHn8fPNIEYdziQgYV0+ce4r****",
        "DecryptKey": "CA4AAEFLh2SUfMm1UIuxgTiD7g/rBKlxShZ/5jxRByvOqgtNyx42vDGACEtPYZwOXIhCn5wnpnisA+ZlDd3oYnmbqO9cG9KzczsHy3x6ymlpsIfw46PB2ostz755947ykzbi71G0osTvqo9rJFrVjQpcfu7/P8iKbJhI5Y/W2CIMuhJdbrQhXl6xyYILnhAV+o4D+c3MgzpkzB/zovcWr7EoDCDcLn4730e96ubN/+BbA9opz11jMymCJsx6PHsrfd6jzis4ZzDV+Uq+jYd5IJHGhmXaaQoeX2Shi+b9lOHuLi/MFqwjoskNMZ17xhc1e7l3lHpQQMIk3dYfnK3UI+6t2D3QoRCz8ytRCfI2uRHtKGZUPYl2586iJ+48lJ7/cWLlRwsypYUv73Oas0KnuRqGCilG2dwpIW7P/0CAqO09hN7ti+IMbiVK4dHF8aJB24A9SAHpN1ZejZbduHACPBDfb5jyMdBegswiip9qdtRzHfP9CU1ozVRXt/8jt0iJdib6nEuvjHuFDauVuZk1ahd9MUqhdK3zKBBHdH9uP+EyiYu8w88+N0XOEMWv****",
        "KeyId": "1c820b96-73bd-11ea-a490-5254006d0810"
    }
}
注意:

  • 目前支持的加密算法有两种:SM4,AES_256。
  • 返回的 EncryptKey,DecryptKey 分别是加密密钥和解密密钥,且都做了 base64 编码。

步骤2:绑定设备指纹(可选操作)

  1. 登录需要采集设备指纹的机器,运行指纹采集工具(getdevUUID),得到长度70位的字符串,即为设备指纹。
  2. 通过 密钥管理系统控制台API 的方式,将收集到的设备指纹列表上传并绑定到指定的白盒密钥上。
说明:

指纹采集工具(getdevUUID)请直接从 密钥管理系统控制台 上下载。

请求命令:

tccli kms OverwriteWhiteboxDeviceFingerprints --region ap-guangzhou --KeyId 1c820b96-73bd-11ea-a490-5254006d****  --DeviceFingerprints '[{"Identity":"c19c024c-2ba1-11b2-a85c-96f970f4****","Description":"10.0.0.1 密钥管理系统设备"}]'

返回结果示例:

{
    "Response": {
        "RequestId": "55f7ca05-17af-4bfb-87a5-80a80a4b0761",
     }
}
注意:

DeviceFingerprints 会完全替换现有指定密钥的指纹。如果 DeviceFingerprints 为空,则表示删除该密钥所关联的所有的设备指纹。

步骤3:对明文进行 base64 编码

准备需要使用白盒密钥管理的明文,并对该明文进行 base64 编码。 例如,假设要加密的明文是:1234567890,使用 openssl 命令生成 base64 编码后的结果为:MTIzNDU2Nzg5MAo=

echo 1234567890 | openssl base64

步骤4:使用白盒密钥加密 API Key

请求命令:

tccli kms EncryptByWhiteBox --region ap-guangzhou --KeyId a1a9376a-7261-11ea-a490-5254006d**** --PlainText MTIzNDU2Nzg5MAo=

返回结果:

{
    "Response": {
        "RequestId": "1bf315d1-3b20-4089-b458-51c367967b4b",
        "InitializationVector": "EUi3Vv7DiCf73D6XbVzMYg==",
        "CipherText": "HKyXV1Xoodi1P/sdf/cYLw=="
    }
}
注意:

返回的字段主要有两个:

  • InitializationVector:随机生成的初始化向量(简称 Iv),用于增加密文被破解的难度。
  • CipherText:加密后的密文,并进行了 base64 编码。

步骤5:白盒解密密钥和 API Key 密文分发

管理员将上述步骤中生成的 DecryptKey,InitializationVector,CipherText 分发给各业务系统的开发或运维人员。其中,DecryptKey 会被部署到相应业务系统的文件中,而 InitializationVector 和 CipherText 会作为 SDK 的传参。

其中:

  1. 解密密钥(DecryptKey)需要以二进制文件的形式保存,供系统启动的时候读取。操作命令如下:
    1. 将 DecryptKey 保存到文件中。
      echo "CA4AAEFLh2SUfMm1UIuxgTiD7g/rBKlxShZ/5jxRByvOqgtNyx42vDGACEtPYZwOXIhCn5wnpnisA+ZlDd3oYnmbqO9cG9KzczsHy3x6ymlpsIfw46PB2ostz755947ykzbi71G0osTvqo9rJFrVjQpcfu7/P8iKbJhI5Y/W2CIMuhJdbrQhXl6xyYILnhAV+o4D+c3MgzpkzB/zovcWr7EoDCDcLn4730e96ubN/+BbA9opz11jMymCJsx6PHsrfd6jzis4ZzDV+Uq+jYd5IJHGhmXaaQoeX2Shi+b9lOHuLi/MFqwjoskNMZ17xhc1e7l3lHpQQMIk3dYfnK3UI+6t2D3QoRCz8ytRCfI2uRHtKGZUPYl2586iJ+48lJ7/cWLlRwsypYUv73Oas0KnuRqGCilG2dwpIW7P/0CAqO09hN7ti+IMbiVK4dHF8aJB24A9SAHpN1ZejZbduHACPBDfb5jyMdBegswiip9qdtRzHfP9CU1ozVRXt/8jt0iJdib6nEuvjHuFDauVuZk1ahd9MUqhdK3zKBBHdH9uP+EyiYu8w88+N0XOEMWv****" > decrypt_key.base64
      2. base64 解码获取公钥实际内容:
      openssl enc -d -base64 -A -in decrypt_key.base64 -out decrypt_key.bin
      3. 将生成的二进制文件 decrypt_key.bin 放在和业务系统(已经集成了解密 SDK)相同的服务器上的指定的目录`decrypt_key_bin_dir`中。
  2. 将 InitializationVector,CipherText,decrypt_key_bin_dir,decrypt_key.bin 以字符串的形式,作为 SDK 中的 whitebox_decrypt 方法的传入参数。

步骤6:部署白盒解密密钥

各业务系统根据自身的编程语言,选择下载相应编程语言的解密 SDK,将 SDK 集成到业务系统中。

步骤7:使用 API Key 密文

在业务逻辑中调用 SDK 的解密函数(whitebox_decrypt),传入参数:decrypt_key_bin_dir,decrypt_key.bin,InitializationVector,CipherText,algorithmType 从而获得解密后的明文。其中 algorithmType 是生成密钥时使用的算法类型。取值为0或1。0表示 AES_256,1表示 SM4。

代码示例

下面以 Golang,Python,C,Java 这四种语言,分别提供相应的示例代码:

基于 Golang 的解密代码示例

package main

import (
   "encoding/base64"
   "fmt"
   "unsafe"
)

/*
#cgo CFLAGS: -I./include
#cgo LDFLAGS: -L./lib -lydwbcrypto
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "wrp.h"

static int clt_load_key(WRP_KEY_CTX *key_ctx, char *decrypt_key_path, const char *file_name, uint32_t mode, uint32_t algoType) {
   printf("begin to load key in dir: %s\n", decrypt_key_path);
    printf("begin to load key: %s\n", file_name);
    int err;
    const WRP_KEY* keyalg = WRP_KEY_wbaes();
   if (algoType == 1) { keyalg = WRP_KEY_wbsm4(); }

    err = WRP_KEY_init(key_ctx, keyalg, 0);
    if (err) { printf("Err: orig_byte init\n"); goto end; }

    err = WRP_KEY_ctrl(key_ctx, WRP_KEY_CTRL_WB_SET_PATH, decrypt_key_path, strlen(decrypt_key_path));
    if (err) { printf("Err: set path\n"); goto end; }

    err = WRP_KEY_import(key_ctx, file_name, mode);
    if (err) { printf("Err: WRP_KEY_import error: %d, \n", err); goto end; }

end:
    return err;
}


int demo_clt_cbc_dec(char * file_dir, const char * file_name, uint8_t* ciph, uint32_t ciphlen, char* iv, uint32_t algoType, uint8_t* out, uint32_t* outlen) {
    WRP_KEY_CTX *mykey;
    WRP_CIPHER_CTX *myaes = NULL;

    uint32_t bits;
    ERRNO err = ERRNO_OK;

    const WRP_CIPHER* alg = WRP_wbaes_cbc();
   if (algoType == 1) { alg = WRP_wbsm4_cbc(); }

    // IO: load wbkey
    mykey = WRP_KEY_CTX_new();
    err = clt_load_key(mykey, file_dir, file_name, KEYMODE_DECRYPT, algoType);
    if (err) { printf("load whitebox key error: %d\n", err); goto cleanup; }

    printf("load key success\n");
    bits = WRP_KEY_key_len(mykey, KEYLEN_TYPE_BITS);
    printf("key len=%u\n", bits);

    if (bits == 0) { printf("get whitebox key length error\n"); err=-1; goto cleanup; }

    // crypto
    myaes = WRP_CIPHER_CTX_new();

    err = WRP_CIPHER_Decrypt_init(myaes, alg, mykey, iv);
    if (err) { printf("Dec: init Err**\n"); goto cleanup; }
    printf("decrypt init success, begin to decrypt cipher\n");

    err = WRP_CIPHER_Decrypt_doCipher(myaes, ciph, ciphlen, out, outlen);
    if (err) { printf("decrypt error: %.8X\n", err); goto cleanup; }
    //printf("decrypt success\n");
    //printf("output: %s\n", deout);
    //printf("\n");

cleanup:
    WRP_KEY_CTX_free(mykey);
    WRP_CIPHER_CTX_free(myaes);
    return err;
}
*/
import "C"

/**
algorithmType = 0 : AES_256
algorithmType = 1 : SM4
*/
func whitebox_decrypt(decrypt_key_bin_dir string, fileName string, Iv string, CipherText string, algorithmType int) {
   cipher, _ := base64.StdEncoding.DecodeString(CipherText)
   iv, _ := base64.StdEncoding.DecodeString(Iv)

   whitebox_decrypt_key_dir := C.CString(decrypt_key_bin_dir)
   defer C.free(unsafe.Pointer(whitebox_decrypt_key_dir))

   whitebox_decrypt_key := C.CString(fileName)
   defer C.free(unsafe.Pointer(whitebox_decrypt_key))

   //malloc a buffer which size is a little greater than your plaintext
   out_c := C.malloc(C.sizeof_char * 1024)
   defer C.free(unsafe.Pointer(out_c))
   outlen_c := 1024

   err_c := C.demo_clt_cbc_dec(whitebox_decrypt_key_dir, whitebox_decrypt_key,
      (*C.uchar)(unsafe.Pointer(&cipher[0])),
      C.uint(len(cipher)),
      (*C.char)(unsafe.Pointer(&iv[0])),
      C.uint(algorithmType),
      (*C.uchar)(out_c),
      (*C.uint)(unsafe.Pointer(&outlen_c)))
   if err_c != 0 {
      h := fmt.Sprintf("%X", err_c)
      fmt.Printf("decrypt error, error code: %d(hex %s)\n", err_c, h)
   } else {
      //plain := C.GoBytes(out_c, C.int(outlen_c))
      plain := C.GoString((*C.char)(out_c))
      fmt.Println(string(plain))
      fmt.Println("decrypt success")
      fmt.Println("output: ", plain)
      fmt.Println()
   }
}

func main() {
   fmt.Println("--------- test case for AES_256 ---------")
   // 解密密钥文件所在的目录
   decryptKeyFileDirectoryName := "./data"
   // 解密密钥文件名
   decryptKeyFileName := "decrypt_key_aes256.bin"
   // 初始化向量,base64编码
   iv := "EUi3Vv7DiCf73D6XbVzMYg=="
   // 白盒密钥加密后的密文,并base64编码
   cipherText := "HKyXV1Xoodi1P/sdf/cYLw=="
   // 创建白盒密钥时用的加密算法, 0: AES_256, 1: SM4
   algoType := 0
   fmt.Println(fmt.Sprintf("demo start for decryptKeyFileName=%s, iv=%s, cipherText=%s, algoType=%d", decryptKeyFileName, iv, cipherText, algoType))

   whitebox_decrypt(decryptKeyFileDirectoryName, decryptKeyFileName, iv, cipherText, algoType)

   fmt.Println("--------- test case for SM4 ---------")
   // 解密密钥文件所在的目录
   decryptKeyFileDirectoryName = "./data"
   // 解密密钥文件名
   decryptKeyFileName = "decrypt_key_sm4.bin"
   // 初始化向量,base64编码
   iv = "9+COkyNOrT8mvWN6CgTjKw=="
   // 白盒密钥加密后的密文,并base64编码
   cipherText = "83ji4vKFwtVSAN1LSh1aOQ=="
   // 创建白盒密钥时用的加密算法, 0: AES_256, 1: SM4
   algoType = 1
   fmt.Println(fmt.Sprintf("demo start for decryptKeyFileName=%s, iv=%s, cipherText=%s, algoType=%d", decryptKeyFileName, iv, cipherText, algoType))

   whitebox_decrypt(decryptKeyFileDirectoryName, decryptKeyFileName, iv, cipherText, algoType)

   fmt.Println("--------- test cases finished ---------")
}

基于 Python 的解密代码示例

#!/usr/bin/python

import os
from ctypes import *
import base64

ydwbcrypto = CDLL('../lib/libydwbcrypto.so')
ydwbcrypto.WRP_KEY_CTX_new.restype  = POINTER(c_int)
ydwbcrypto.WRP_KEY_wbsm4.restype  = POINTER(c_int)
ydwbcrypto.WRP_CIPHER_CTX_new.restype = POINTER(c_int)
ydwbcrypto.WRP_wbsm4_cbc.restype = POINTER(c_int)
KEYMODE_ENCRYPT = 4
KEYMODE_DECRYPT = 8
WRP_KEY_CTRL_WB_SET_PATH = 0x0003
KEYLEN_TYPE_BITS = 0

ydwbcrypto.WRP_KEY_wbaes.restype  = POINTER(c_int)
ydwbcrypto.WRP_wbaes_cbc.restype = POINTER(c_int)

def clt_load_key(key_ctx, decrypt_key_path, file_name, mode, algoType):
    print ("begin to load key: %s"%(file_name))
    keyalg = ydwbcrypto.WRP_KEY_wbaes()
    if algoType == 1:
        keyalg = ydwbcrypto.WRP_KEY_wbsm4()

    err = ydwbcrypto.WRP_KEY_init(key_ctx, keyalg, 0)
    if (err):
        raise YdwbCryptoException("Err: orig_byte init")

    err = ydwbcrypto.WRP_KEY_ctrl(key_ctx, WRP_KEY_CTRL_WB_SET_PATH, decrypt_key_path, len(decrypt_key_path))
    if (err):
        raise YdwbCryptoException("Err: set path")

    err = ydwbcrypto.WRP_KEY_import(key_ctx, file_name, mode)
    if (err):
        raise YdwbCryptoException("Err: WRP_KEY_import error: %.08X"%(err))

    pass

def demo_clt_cbc_dec(file_dir, file_name, ciph, iv, algoType):

    # IO: load wbkey
    mykey = ydwbcrypto.WRP_KEY_CTX_new()
    err = clt_load_key(mykey, file_dir, file_name, KEYMODE_DECRYPT, algoType)
    if (err):
        ydwbcrypto.WRP_KEY_CTX_free(mykey)
        raise YdwbCryptoException("load whitebox key error: %d"%(err))

    print ("load key success")
    bits = ydwbcrypto.WRP_KEY_key_len(mykey, KEYLEN_TYPE_BITS)
    print ("key len=%u"%(bits))

    if (bits == 0):
        ydwbcrypto.WRP_KEY_CTX_free(mykey)
        raise YdwbCryptoException("get whitebox key length error")

    # crypto
    myaes = ydwbcrypto.WRP_CIPHER_CTX_new()
    alg = ydwbcrypto.WRP_wbaes_cbc()
    if algoType == 1:
        alg = ydwbcrypto.WRP_wbsm4_cbc()
    err = ydwbcrypto.WRP_CIPHER_Decrypt_init(myaes, alg, mykey, iv)
    if (err):
        ydwbcrypto.WRP_KEY_CTX_free(mykey)
        ydwbcrypto.WRP_CIPHER_CTX_free(myaes)
        raise YdwbCryptoException("Dec: init Err**")

    print ("decrypt init success, begin to decrypt cipher")

    outlen = c_int(128)
    outlen_p = pointer(outlen)
    #create a buffer which size is a little greater than your plaintext
    deout = create_string_buffer(128)
    err = ydwbcrypto.WRP_CIPHER_Decrypt_doCipher(myaes, ciph, len(ciph), deout, outlen_p)
    if (err):
        ydwbcrypto.WRP_KEY_CTX_free(mykey)
        ydwbcrypto.WRP_CIPHER_CTX_free(myaes)
        raise YdwbCryptoException("decrypt error: %.8X"%(err))
    #print ("decrypt success")
    #print ("output: %s"%(deout))
    #print sizeof(deout), repr(deout.raw)

    ydwbcrypto.WRP_KEY_CTX_free(mykey)
    ydwbcrypto.WRP_CIPHER_CTX_free(myaes)
    return deout.raw

class YdwbCryptoException(Exception):
    def __init__(self, msg):
        self.msg = msg

if __name__ == "__main__":
    print("--------- test case for AES_256 ---------")
    # 解密密钥文件所在的目录
    decryptKeyFileDirectoryName = "../data";
    # 解密密钥文件名
    decryptKeyFileName = "decrypt_key_aes256.bin"
    # 初始化向量,base64 编码
    iv = "EUi3Vv7DiCf73D6XbVzMYg=="
    # 白盒密钥加密后的密文,并 base64 编码
    cipherText = "HKyXV1Xoodi1P/sdf/cYLw=="
    # 创建白盒密钥时用的加密算法, 0: AES_256, 1: SM4
    algoType = 0
    try:
        print("decryptKeyFileName=%s, iv=%s, cipherText=%s, algoType=%d" % (decryptKeyFileName, iv, cipherText, algoType))
        plain = demo_clt_cbc_dec(decryptKeyFileDirectoryName, decryptKeyFileName, base64.b64decode(cipherText), base64.b64decode(iv), algoType)
        print ("decrypt success")
        print ("plain: %s" % plain)
    except YdwbCryptoException as e:
        print (e.msg)

    print("--------- test case for SM4 ---------")
    # 解密密钥文件所在的目录
    decryptKeyFileDirectoryName = "../data";
    # 解密密钥文件名
    decryptKeyFileName = "decrypt_key_sm4.bin"
    # 初始化向量,base64 编码
    iv = "9+COkyNOrT8mvWN6CgTjKw=="
    # 白盒密钥加密后的密文,并 base64 编码
    cipherText = "83ji4vKFwtVSAN1LSh1aOQ=="
    # 创建白盒密钥时用的加密算法, 0: AES_256, 1: SM4
    algoType = 1
    try:

        print("decryptKeyFileName=%s, iv=%s, cipherText=%s, algoType=%d" % (decryptKeyFileName, iv, cipherText, algoType))
        plain = demo_clt_cbc_dec(decryptKeyFileDirectoryName, decryptKeyFileName, base64.b64decode(cipherText), base64.b64decode(iv), algoType)
        print ("decrypt success")
        print ("plain: %s" % plain)
    except YdwbCryptoException as e:
        print (e.msg)

    print("--------- test case finished  ---------")

    pass

基于 C 的解密代码示例

分为 Windows 和 Linux 两个平台代码:

  • Windows 平台:打开 vs 文件夹下面的 demo.sln,demo 使用的是 vs2017,有静态链接和动态链接两个 demo,直接编译和运行即可。因为路径问题,如果在命令行中使用 exe,需要将 /data 目录拷贝到上一层目录。
  • Linux 平台:需要将 lib 加入环境变量:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib 

示例代码如下:

#include "../include/wrp.h"
#include "base64.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>

static char *decrypt_key_path = "../../data";

#define ALG_AES     1
#define ALG_SM4     2
#define TEST_BUFF_SIZE 100

static int clt_load_key(int alg_id, WRP_KEY_CTX *key_ctx, const char *key_file_path, uint32_t mode) 
{
    printf("begin to load key: %s\n", key_file_path);
    ERRNO err;

    const WRP_KEY*  myalg = NULL;

    switch (alg_id)
    {
    case ALG_AES:
        myalg = WRP_KEY_wbaes();
        break;
    case ALG_SM4:
        myalg = WRP_KEY_wbsm4();
        break;
    default:
        printf("algorithm not support\n");
        return -1;
    }
    if (myalg == NULL)
    {
        printf("encrypt algorithm invalidate. algorithm:%d.\n", alg_id);
        return -1;
    }

    err = WRP_KEY_init(key_ctx, myalg, 0);
    if (err) 
    { 
        printf("Err: orig_byte init\n"); 
        goto end; 
    }

    err = WRP_KEY_ctrl(key_ctx, WRP_KEY_CTRL_WB_SET_PATH, decrypt_key_path, (uint32_t)strlen(decrypt_key_path));
    if (err) 
    { 
        printf("Err: set path\n"); 
        goto end; 
    }

    err = WRP_KEY_import(key_ctx, key_file_path, mode);
    if (err) 
    { 
        printf("Err: WRP_KEY_import error: %d, \n", err); 
        goto end; 
    }

end:
    return err;
}


void whitebox_decrypt(int alg_id, const char * key_file_path, uint8_t* ciph, uint32_t ciphlen, char* iv) 
{
    WRP_KEY_CTX *mykey;
    WRP_CIPHER_CTX *myaes = NULL;

    uint8_t deout[128] = { 0 };
    uint32_t outlen = sizeof(deout);
    uint32_t bits;
    ERRNO err = ERRNO_OK;

    const WRP_CIPHER* myalg = NULL;

    switch (alg_id)
    {
    case ALG_AES:
        myalg = WRP_wbaes_cbc();
        break;
    case ALG_SM4:
        myalg = WRP_wbsm4_cbc();
        break;
    default:
        printf("algorithm not support\n"); 
        return;
    }
    if (myalg == NULL)
    {
        printf("encrypt algorithm invalidate.algorithm:%d.\n", alg_id);
        return;
    }

    mykey = WRP_KEY_CTX_new();
    err = clt_load_key(alg_id, mykey, key_file_path, KEYMODE_DECRYPT);
    if (err) 
    { 
        printf("load whitebox key error: %d\n", err); 
        goto cleanup; 
    }

    printf("load key success\n");
    bits = WRP_KEY_key_len(mykey, KEYLEN_TYPE_BITS);
    printf("key len=%u\n", bits);

    if (bits == 0) 
    { 
        printf("get whitebox key length error\n"); 
        goto cleanup; 
    }

    // crypto
    myaes = WRP_CIPHER_CTX_new();

    err = WRP_CIPHER_Decrypt_init(myaes, myalg, mykey, iv);
    if (err) 
    { 
        printf("Dec: init Err**\n"); 
        goto cleanup; 
    }
    printf("decrypt init success, begin to decrypt cipher\n");

    err = WRP_CIPHER_Decrypt_doCipher(myaes, ciph, ciphlen, deout, &outlen);
    if (err) 
    { 
        printf("decrypt error: %.8X\n", err); 
        goto cleanup; 
    }
    printf("decrypt success\n");
    printf("output: %s\n", deout);
    printf("\n");

cleanup:
    WRP_KEY_CTX_free(mykey);
    WRP_CIPHER_CTX_free(myaes);
}

int demo_aes() 
{
    unsigned char cipher_base64[TEST_BUFF_SIZE] = "snPqPZaFN9CQc5WH/Tx5jA==";  //replace as your own cipher
    unsigned char cipher[TEST_BUFF_SIZE] = {0};
    int cipher_len = Base64decode(cipher, cipher_base64);
    if (cipher_len > TEST_BUFF_SIZE)
    {
        printf("base64 decode cipher text failed, memory is not enough.\n");
        return (-1);
    }

    unsigned char iv_base64[TEST_BUFF_SIZE] = "WBbaiNLcEYSbjKxoJt66UQ==";  //replace as your own iv
    unsigned char iv_bin[TEST_BUFF_SIZE] = { 0 };
    int iv_len = Base64decode(iv_bin, iv_base64);

    if (iv_len != 16)
    {
        printf("iv is not invalidate.\n");
        return (-1);
    }


    char * whitebox_decrypt_key = "decrypt_key_aes256.bin";   //replace as your own key
    whitebox_decrypt(ALG_AES, whitebox_decrypt_key, cipher, cipher_len, iv_bin);

    return 0;
}

int demo_sm4() 
{
    unsigned char cipher_base64[TEST_BUFF_SIZE] = "IwNgzruYfHQ6oQz2PLdyRQ=="; //replace as your own cipher
    unsigned char cipher[TEST_BUFF_SIZE] = {0};
    int cipher_len = Base64decode(cipher, cipher_base64);
    if (cipher_len > TEST_BUFF_SIZE)
    {
        printf("base64 decode cipher text failed, memory is not enough.\n");
        return (-1);
    }

    unsigned char iv_base64[TEST_BUFF_SIZE] = "4qaj6cVd8msMVBqNTRG4Pg==";  //replace as your own iv
    unsigned char iv_bin[TEST_BUFF_SIZE] = {0};
    int iv_len = Base64decode(iv_bin, iv_base64);

    if (iv_len != 16)
    {
        printf("iv is not invalidate.\n");
        return (-1);
    }

    char * whitebox_decrypt_key = "decrypt_key_sm4.bin";   //replace as your own key
    whitebox_decrypt(ALG_SM4, whitebox_decrypt_key, cipher, cipher_len, iv_bin);

    return 0;
}

int main(int argc, const char *argv[]) 
{
    demo_aes();
    demo_sm4();
    return 0;
}

基于 Java 的解密代码示例

import com.tencent.yunding.lightjce.CipherWhiteBox;
import com.tencent.yunding.lightjce.params.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Base64;

public class Main {

    public static void main(String[] args) throws Exception {
        System.out.println("--------- test case for AES_256 ---------");
        // 解密密钥文件所在的目录
        String decryptKeyFileDirectoryName = "../data";
        // 解密密钥文件名
        String decryptKeyFileName = "decrypt_key_aes256.bin";
        // 初始化向量,base64编码
        String iv = "EUi3Vv7DiCf73D6XbVzMYg==";
        // 白盒密钥加密后的密文,并 base64 编码
        String cipherText = "HKyXV1Xoodi1P/sdf/cYLw==";
        // 创建白盒密钥时用的加密算法, 0: AES_256, 1: SM4
        int algoType = 0;
        System.out.printf("demo start for decryptKeyFileName=%s, iv=%s, cipherText=%s, algoType=%d \n", decryptKeyFileName, iv, cipherText, algoType);
        whitebox_decrypt(decryptKeyFileDirectoryName, decryptKeyFileName, iv, cipherText, algoType);

        System.out.println("--------- test case for SM4 ---------");
        // 解密密钥文件所在的目录
        decryptKeyFileDirectoryName = "../data";
        // 解密密钥文件名
        decryptKeyFileName = "decrypt_key_sm4.bin";
        // 初始化向量,base64 编码
        iv = "9+COkyNOrT8mvWN6CgTjKw==";
        // 白盒密钥加密后的密文,并 base64 编码
        cipherText = "83ji4vKFwtVSAN1LSh1aOQ==";
        // 创建白盒密钥时用的加密算法, 0: AES_256, 1: SM4
        algoType = 1;
        System.out.printf("demo start for decryptKeyFileName=%s, iv=%s, cipherText=%s, algoType=%d \n", decryptKeyFileName, iv, cipherText, algoType);

        whitebox_decrypt(decryptKeyFileDirectoryName, decryptKeyFileName, iv, cipherText, algoType);

        System.out.println("--------- test case finished  ---------");

    }

    public static void whitebox_decrypt(String decrypt_key_bin_dir, String fileName, String Iv, String CipherText, int algorithmType) throws Exception {
        byte[] cipher = Base64.getDecoder().decode(CipherText);
        byte[] iv = Base64.getDecoder().decode(Iv);
        String decryptKeyFilePath = decrypt_key_bin_dir + "/" + fileName;

        if (algorithmType == 0) {
            byte[] result1 = decAESData(decryptKeyFilePath, cipher, iv);
            System.out.println("AES decrypted text length: " + result1.length);
            System.out.println("AES decrypted text       : " + new String(result1));
        } else if (algorithmType == 1) {
            byte[] result2 = decSM4Data(decryptKeyFilePath, cipher, iv);
            System.out.println("SM4 decrypted text length: " + result2.length);
            System.out.println("SM4 decrypted text       : " + new String(result2));
        }
    }

    public static byte[] readFile(String fileName) throws Exception {
        File file = new File(fileName);
        FileInputStream fileInputStream = new FileInputStream(file);
        if (file.canRead()) {
            try {
                int available = fileInputStream.available();
                byte[] result = new byte[available];
                int read = fileInputStream.read(result);
                return result;
            } finally {
                if (null != fileInputStream) {
                    fileInputStream.close();
                }
            }
        } else {
            throw new Exception("file can not be read.");
        }
    }

    public static void saveFile(String fileName, byte[] fileStream) throws Exception {
        File file = new File(fileName);
        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
            boolean mkdirs = parentFile.mkdirs();
        }
        if (!file.exists()) {
            boolean newFile = file.createNewFile();
        }
        if (file.canWrite()) {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            try {
                fileOutputStream.write(fileStream);
            } finally {
                if (null != fileOutputStream) {
                    fileOutputStream.close();
                }
            }
        } else {
            throw new Exception("file can not be write.");
        }
    }

    public static byte[] decAESData(String keyFilePath, byte[] data, byte[] iv) throws Exception {
        CipherWhiteBox instance = CipherWhiteBox.getInstance(SymAlgType.AES, BlockMode.cbc_mode, PaddingMode.p5padding);
        File file = new File(keyFilePath);
        instance.init(file, iv, CryptMode.decrypt_mode);
        instance.update(data);
        return instance.doFinal();
    }

    public static byte[] decSM4Data(String keyFilePath, byte[] data, byte[] iv) throws Exception {
        CipherWhiteBox instance = CipherWhiteBox.getInstance(SymAlgType.SM4, BlockMode.cbc_mode, PaddingMode.p5padding);
        File file = new File(keyFilePath);
        instance.init(file, iv, CryptMode.decrypt_mode);
        instance.update(data);
        return instance.doFinal();
    }

}
目录