抽象化国密SKF接口

博客指出国密SKF接口难用,原因是接口定义太“散”,未抽象出类似对象接口编程,推测是因接口定义专家偏理论算法。博主闲来对其进行抽象,给出了头文件和C文件,以方便开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

概要

国密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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值