概要
国密SKF接口太难用了,使用接口编程实现一个小功能都要写一大堆函数语句,个人认为导致这个问题的原因就是接口定义太“散“了,没有抽象出类似对象接口编程。据说由于SKF接口定义的专家偏理论算法…
所以闲来抽象出一些接口方便开发。
头文件
#ifndef __GTK_UKEY_H__
#define __GTK_UKEY_H__
/**
* @file gtk_ukey.h
* @brief 基于国密SKF接口进行对:ukey的文件读写、数字信封加解密等功能封装的工具包,按需更新新增
* @author gongwang
* @date 2022-07-17
*/
#include <libukey/SKFAPI.h>
#include <libukey/EncDecFile.h>
#define MAX_CONTAINER_NUM 8
#define MAX_PIN_NUM 65 //64+1
#define CONTAINER_NAME "test_container"
#define GTK_LETTER_CODE 0x01010101
#define LETTER_MAX_LEN 1024 * 8
typedef unsigned char uint8_t;
/* bin和算法结构体的互转 */
int bin_to_ECCCIPHERBLOB(uint8_t *i, int i_len, ECCCIPHERBLOB *s);
int ECCCIPHERBLOB_to_bin(ECCCIPHERBLOB *s, uint8_t **i, int *i_len);
int bin_to_ECCPUBLICKEYBLOB(uint8_t *i, ECCPUBLICKEYBLOB *s);
int ECCPUBLICKEYBLOB_to_bin(ECCPUBLICKEYBLOB *s, uint8_t i[64]);
/* end */
/**
* @brief 操作等级
* 1.gtk_ukey_new的入参
* 2.由操作等级,决定是否打开app,打开容器
* 3.clean时将根据等级释放空间
*/
typedef enum UKEY_OL
{
K_HAND, //connect dev
K_APP, //open app
K_CONTAINER, //open container
} UKEY_OL_T;
typedef struct gtk_ukey
{
HANDLE dev;
HAPPLICATION app;
HCONTAINER container;
char pin[MAX_PIN_NUM];
UKEY_OL_T ol;
} gtk_ukey_t;
//############################ ukey操作函数,由操作等级划分 ###########################
void _print_skf_string_list(const char *prifex, char *list, int list_len);
/**
* @brief 创建ukey操作句柄
*
* @param ol 句柄的操作等级,根据应用场景传入
* @param pin 可选,若操作等级大于K_APP,必须传入pin
* @return gtk_ukey_t*
*/
gtk_ukey_t *gtk_ukey_new(UKEY_OL_T ol, char *pin);
/**
* @brief 将根据k的操作等级释放ukey资源
*/
void gtk_ukey_clean(gtk_ukey_t *k);
/**
* @brief 阻塞监听ukey拔插事件
* @return 1:插入key , 2: 拔出key
*/
int gtk_ukey_event(void);
/**
* @brief 获取ukey的SN
* @param k ,需 k->UKEY_OL>= K_HAND
* @return char*
*/
char *gtk_ukey_sn(gtk_ukey_t *k);
/**
* @brief 从ukey中读取文件
*
* @param k ,需 k->UKEY_OL>= K_APP
* @param filename ukey中保存的文件
* @param out 读取的文件数据
* @param outlen 文件数据长度
* @return int 0 is sucess
*/
int gtk_ukey_fread(gtk_ukey_t *k, char *filename, uint8_t **out, int *outlen);
/**
* @brief 从ukey中写入文件
*
* @param k ,需 k->UKEY_OL>= K_APP
* @param filename 将ukey中保存的文件
* @param in 带写入的文件数据
* @param inlen 文件数据长度
* @return int 0 is success
*/
int gtk_ukey_fwrite(gtk_ukey_t *k, char *filename, uint8_t *in, int inlen);
//数字信封接口
/**
* @brief 数字信封加密
*
* @param k ,需k->UKEY_OL >= K_CONTAINER
* @param raw_data 需加密的原始数据
* @param raw_len 原始数据长度
* @return void* 加密后的密文
*/
void *gtk_letter_enc(gtk_ukey_t *k, uint8_t *raw_data, int raw_len);
/**
* @brief 数字信封解密
*
* @param k ,需k->UKEY_OL >= K_CONTAINER
* @param letter 需解密的密文数据
* @param raw_data 装解密后数据的buffer
* @param raw_len 解密数据的长度
* @return int 0 is dec success
*/
int gtk_letter_dec(gtk_ukey_t *k, void *letter, uint8_t raw_data[LETTER_MAX_LEN], int *raw_len);
//获取密文长度
int gtk_letter_length(void *letter);
//释放密文空间
void gtk_letter_clean(void *letter);
#endif /* __GTK_UKEY_H__ */
C文件
/**
* @file gtk_ukey.c
* @brief 基于国密SKF接口进行对:ukey的文件读写、数字信封加解密等功能封装的工具包,按需更新新增
* @author gongwang
* @date 2022-07-17
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <libukey/SKFAPI.h>
#include <libukey/EncDecFile.h>
#include "gtk_ukey.h"
//skf string list 由\0分隔符,由\0\0结束;
void _print_skf_string_list(const char *prifex, char *list, int list_len)
{
int i = 0;
int feed = 0;
char *p = NULL;
p = list;
while (feed < list_len)
{
printf(">>> %slist[%d]:%s \n", prifex, i++, p);
feed += strlen(p) + 1;
p = list + feed;
if (*p == '\0')
break;
}
}
gtk_ukey_t *gtk_ukey_new(UKEY_OL_T ol, char *pin)
{
char dev_list[128] = { 0 };
char app_list[128] = { 0 };
char container_list[512] = { 0 };
ULONG list_len = 0;
DWORD dwRet = 0;
list_len = 128;
dwRet = SKF_EnumDev(TRUE, dev_list, &list_len);
if (SAR_OK != dwRet)
{
gtk_error("SKF api failed, errmsg:%s \n", show_error(dwRet));
return NULL;
}
if (0 == strlen(dev_list))
{
gtk_error("dont have ukey");
return NULL;
}
gtk_ukey_t *ret = (gtk_ukey_t *)malloc(sizeof(gtk_ukey_t));
if (NULL == ret)
return NULL;
memset(ret, 0x0, sizeof(gtk_ukey_t));
if (ol < K_HAND || ol > K_CONTAINER)
{
ol = K_HAND;
}
ret->ol = ol;
if (NULL != pin)
{
strncpy(ret->pin, pin, MAX_PIN_NUM);
ret->pin[MAX_PIN_NUM - 1] = '\0';
}
/*connect dev*/
dwRet = SKF_ConnectDev(dev_list, &(ret->dev));
if (SAR_OK != dwRet)
{
gtk_error("SKF failed, errmsg:%s \n", show_error(dwRet));
goto err;
}
/*open app*/
if (--ol >= 0)
{
DWORD retry_num = 0;
list_len = 128;
//目前是一定有个且仅一个app,直接读出并打开
SKF_EnumApplication(ret->dev, app_list, &list_len);
dwRet = SKF_OpenApplication(ret->dev, app_list, &(ret->app));
if (SAR_OK != dwRet)
{
gtk_error("SKF failed, errmsg:%s \n", show_error(dwRet));
goto err;
}
dwRet = SKF_VerifyPIN(ret->app, USER_TYPE, ret->pin, &retry_num);
if (SAR_OK != dwRet)
{
gtk_error("SKF failed, errmsg:%s \n", show_error(dwRet));
goto err;
}
/*open container*/
if (--ol >= 0)
{
list_len = 512;
SKF_EnumContainer(ret->app, container_list, &list_len);
if (NULL == strstr(container_list, CONTAINER_NAME))
{
SKF_CreateContainer(ret->app, CONTAINER_NAME, &(ret->container));
}
dwRet = SKF_OpenContainer(ret->app, CONTAINER_NAME, &(ret->container));
if (SAR_OK != dwRet)
{
gtk_error("SKF failed, "
"errmsg:%s \n",
show_error(dwRet));
goto err;
}
}
}
return ret;
err:
if (ret->app)
SKF_CloseApplication(ret->app);
if (ret->dev)
SKF_DisConnectDev(ret->dev);
free(ret);
return NULL;
}
void gtk_ukey_clean(gtk_ukey_t *k)
{
if (NULL == k)
return;
switch (k->ol)
{
case K_CONTAINER:
SKF_CloseContainer(k->container);
case K_APP:
SKF_CloseApplication(k->app);
case K_HAND:
SKF_DisConnectDev(k->dev);
break;
default:
break;
}
free(k);
k = NULL;
return;
}
//监听ukey拔出插入事件 ,1 is in , 2 is out
int gtk_ukey_event(void)
{
int event = 0;
char dev_name[128] = { 0 };
DWORD name_len = 128;
SKF_WaitForDevEvent(dev_name, &name_len, (ULONG *)&event);
gtk_debug("ukey event :%d \n", event);
return event;
}
char *gtk_ukey_sn(gtk_ukey_t *k)
{
DWORD dwRet = 0;
DEVINFO dev_info;
char *ukey_sn = NULL;
if (NULL == k || k->ol < K_HAND)
return NULL;
dwRet = SKF_GetDevInfo(k->dev, &dev_info);
if (SAR_OK != dwRet)
{
gtk_debug("Failed to get serial number and device "
"label\n");
return NULL;
}
gtk_debug("serial number=%s, device=%s\n", dev_info.SerialNumber, dev_info.Label);
ukey_sn = strdup(dev_info.SerialNumber);
return ukey_sn;
}
int gtk_ukey_fread(gtk_ukey_t *k, char *filename, uint8_t **out, int *outlen)
{
FILEATTRIBUTE fileinfo;
DWORD dwRet = 0;
int file_size = 0;
int offset = 0;
ULONG rest = 0;
if (NULL == k || k->ol < K_APP)
return -1;
memset(&fileinfo, 0x0, sizeof(FILEATTRIBUTE));
dwRet = SKF_GetFileInfo(k->app, filename, &fileinfo);
if (SAR_OK != dwRet)
{
gtk_error("SKF failed, errmsg:%s \n", show_error(dwRet));
return -1;
}
file_size = fileinfo.FileSize;
*out = (uint8_t *)malloc(file_size);
rest = fileinfo.FileSize;
while (offset < file_size)
{
dwRet = SKF_ReadFile(k->app, filename, offset, rest, *out + offset, &rest);
if (SAR_OK != dwRet)
{
printf("SKF_ReadFile dwRet=%s\n\n", show_error(dwRet));
free(*out);
*out = NULL;
*outlen = 0;
return -1;
}
offset += rest;
rest = file_size - offset;
}
*outlen = fileinfo.FileSize;
return 0;
}
int gtk_ukey_fwrite(gtk_ukey_t *k, char *filename, uint8_t *in, int inlen)
{
if (NULL == k || k->ol < K_APP)
return -1;
//offset置0,暂不支持文件追加应用场景
ULONG offset = 0;
DWORD dwRet = 0;
dwRet = SKF_CreateFile(k->app, filename, inlen, SECURE_USER_ACCOUNT, SECURE_USER_ACCOUNT);
printf("SKF_CreateFile dwRet=%s\n\n", show_error(dwRet));
if (SAR_FILE_ALREADY_EXIST == dwRet)
{
SKF_DeleteFile(k->app, filename);
dwRet = SKF_CreateFile(k->app, filename, inlen, SECURE_USER_ACCOUNT, SECURE_USER_ACCOUNT);
if (SAR_OK != dwRet)
{
gtk_error("SKF failed, errmsg:%s \n", show_error(dwRet));
return -1;
}
}
dwRet = SKF_WriteFile(k->app, filename, offset, in, inlen - offset);
if (SAR_OK != dwRet)
{
gtk_error("SKF failed, errmsg:%s \n", show_error(dwRet));
return -1;
}
return 0;
}
/* uint8_t*与结构体的转换 */
//数据填充为结构体
int bin_to_ECCCIPHERBLOB(uint8_t *i, int i_len, ECCCIPHERBLOB *s)
{
//在结构体里面x_len = 32, y_len = 32, hash_len = 32,
void *p = NULL;
p = i;
memset(s, 0x00, sizeof(ECCCIPHERBLOB));
memcpy(s->XCoordinate + 32, p, 32); //前32为空
memcpy(s->YCoordinate + 32, p + 32, 32);
memcpy(s->Hash, p + 64, 32);
s->CipherLen = i_len - 96; //(32+32+32 => x_len, y_len, hash_len)
memcpy(s->Cipher, p + 96, s->CipherLen);
return 0;
}
//结构体填充为数据
int ECCCIPHERBLOB_to_bin(ECCCIPHERBLOB *s, uint8_t **i, int *i_len)
{
int len = 0;
len = s->CipherLen + 96;
*i = (uint8_t *)malloc(len);
memcpy(*i, s->XCoordinate + 32, 32);
memcpy(*i + 32, s->YCoordinate + 32, 32);
memcpy(*i + 64, s->Hash, 32);
// memcpy(*i + 96, &(s->CipherLen), 4);
memcpy(*i + 96, s->Cipher, s->CipherLen);
*i_len = len;
return 0;
}
//i长度被固定为64, 无传入必要
int bin_to_ECCPUBLICKEYBLOB(uint8_t *i, ECCPUBLICKEYBLOB *s)
{
void *p = NULL;
p = i;
memset(s, 0x00, sizeof(ECCPUBLICKEYBLOB));
memcpy(s->XCoordinate + 32, p, 32);
memcpy(s->YCoordinate + 32, p + 32, 32);
return 0;
}
int ECCPUBLICKEYBLOB_to_bin(ECCPUBLICKEYBLOB *s, uint8_t i[64])
{
memcpy(i, s->XCoordinate + 32, 32);
memcpy(i + 32, s->YCoordinate + 32, 32);
return 0;
}
typedef struct gtk_letter
{
int head;
int kcipher_len; //key cipher
int dcipher_len; //data cipher
uint8_t cipher[0];
} gtk_letter_t;
gtk_letter_t *gtk_letter_new(uint8_t *kcipher, int k_len, uint8_t *dcipher, int d_len)
{
gtk_letter_t *ret = NULL;
int len = 0;
if (k_len <= 0 || d_len <= 0)
return NULL;
len = k_len + d_len;
ret = (gtk_letter_t *)malloc(sizeof(struct gtk_letter) + len);
if (NULL == ret)
return NULL;
ret->head = GTK_LETTER_CODE;
ret->kcipher_len = k_len;
ret->dcipher_len = d_len;
memcpy(ret->cipher, kcipher, k_len);
memcpy(ret->cipher + k_len, dcipher, d_len);
return ret;
}
void gtk_letter_clean(void *letter)
{
if (letter)
{
free(letter);
letter = NULL;
}
}
int gtk_letter_length(void *letter)
{
int length = 0;
gtk_letter_t *sletter = NULL;
sletter = (gtk_letter_t *)letter;
length = 12 + sletter->kcipher_len + sletter->dcipher_len;
return length;
}
// int gtk_letter_check(uint8_t *letter, int len)
// {
// void *p = NULL;
// if(len < 12)
// return -1;
// p = (void *)letter;
// if(GTK_LETTER_CODE == *(int *)p)
// return 0;
// return -1;
// }
//数字信封加密
/**
* @brief 使用ukey中用于加解密的ECC密钥对进行数字信封加密
* 警告:加密后,数据解密前,不可再次调用任何使得ukey丢失或重置ECC加解密的密钥对,否则将无法解密
* @param k
* @param raw_data
* @param raw_len 限制最大长度 8*1024 bytes,如有更大数据需求,自行修改
* @return gtk_letter_t*
*/
void *gtk_letter_enc(gtk_ukey_t *k, uint8_t *raw_data, int raw_len)
{
void *letter = NULL;
gtk_letter_t *sletter = NULL;
ULONG sret = SAR_OK;
BLOCKCIPHERPARAM c_param;
HANDLE sm4key = NULL;
//随机数rE
uint8_t rE[32] = { 0 };
//密钥密文
ECCCIPHERBLOB ck;
uint8_t *i_ck = NULL;
int i_ck_len = 0;
//数据密文
uint8_t cd[LETTER_MAX_LEN] = { 0 };
int cd_len = 0;
ULONG bsize = LETTER_MAX_LEN;
if (raw_len > sizeof(cd) - 16) //16:填充的一组长度
{
printf("data_len is too long \n");
return NULL;
}
if (SAR_OK != (sret = SKF_GenRandom(k->dev, rE, 32)))
{
printf("SKF_GenRandom failed : %s \n", show_error(sret));
return NULL;
}
SKFE_ECCEncData(k->container, rE, 32, &ck);
ECCCIPHERBLOB_to_bin(&ck, &i_ck, &i_ck_len);
memset(&c_param, 0x00, sizeof(c_param));
c_param.PaddingType = 1;
//实际此skf接口好像只固定取传入地址的16字节随机数
SKF_SetSymmKey(k->dev, rE, SGD_SMS4_ECB, &sm4key);
SKF_EncryptInit(sm4key, c_param);
SKF_EncryptUpdate(sm4key, raw_data, raw_len, cd, &bsize);
cd_len += bsize;
bsize = LETTER_MAX_LEN - bsize;
if (SAR_OK != (sret = SKF_EncryptFinal(sm4key, cd + cd_len, &bsize)))
{
printf("SKF_EncryptFinal failed : %s \n", show_error(sret));
goto end;
}
cd_len += bsize;
sletter = gtk_letter_new(i_ck, i_ck_len, cd, cd_len);
letter = (void *)sletter;
end:
if (i_ck)
free(i_ck);
return letter;
}
int gtk_letter_dec(gtk_ukey_t *k, void *letter, uint8_t raw_data[LETTER_MAX_LEN], int *raw_len)
{
ULONG sret = SAR_OK;
gtk_letter_t *de = NULL;
ECCCIPHERBLOB ck;
uint8_t rE[32] = { 0 };
ULONG bsize = 32;
uint8_t *i_ck, *i_cd;
int i_ck_len, i_cd_len;
void *p = NULL;
BLOCKCIPHERPARAM c_param;
HANDLE sm4key = NULL;
int offset = 0;
p = letter;
if (GTK_LETTER_CODE != *(int *)p)
{
gtk_error("check letter head failed \n");
return -1;
}
de = (gtk_letter_t *)p;
i_ck = de->cipher;
i_ck_len = de->kcipher_len;
i_cd = de->cipher + i_ck_len;
i_cd_len = de->dcipher_len;
// printf("i_ck_len :%d , id_cd_len : %d \n",i_ck_len, i_cd_len);
bin_to_ECCCIPHERBLOB(i_ck, i_ck_len, &ck);
sret = SKFE_ECCDecData(k->container, &ck, rE, &bsize); //解密rE
if (SAR_OK != sret)
{
printf("SKFE_ECCDecData failed, errmsg:%s \n", show_error(sret));
return -1;
}
memset(&c_param, 0x00, sizeof(c_param));
c_param.PaddingType = 0;
SKF_SetSymmKey(k->dev, rE, SGD_SMS4_ECB, &sm4key);
SKF_DecryptInit(sm4key, c_param);
bsize = LETTER_MAX_LEN;
sret = SKF_DecryptUpdate(sm4key, i_cd, i_cd_len, raw_data, &bsize);
if (SAR_OK != sret)
{
gtk_error("SKF_DecryptUpdate failed !!! ");
return -1;
}
offset += bsize;
bsize = LETTER_MAX_LEN - bsize;
sret = SKF_DecryptFinal(sm4key, raw_data + offset, &bsize);
if (SAR_OK != sret)
{
gtk_error("SKF_DecryptFinal failed !!! ");
return -1;
}
offset += bsize;
*raw_len = offset;
return 0;
}