SKF接口调用

转自https://baijiahao.baidu.com/s?id=1665386000347812466&wfr=spider&for=pc

针对支持国密算法USB KEY设备的应用,国家颁布一个行业标准《智能密码钥匙应用接口规范》(GM/T0016-2012),市面上销售的国密算法的USB KEY设备也都(其实也是必须)支持这个接口规范。因此,只要根据这个规范开发的应用程序,就可以兼容使用不同厂家及品牌的USB KEY产品。由于此规范中函数名称都以SKF开头,所以我们一般把按照此规范提供的设备开发接口库叫做SKF库或SKF接口。

一篇文章不可能介绍完SKF库里的所有接口,今天主要从开发的角度入手,按照实现几个主要功能的目的将涉及的接口和调用方式梳理一下,以帮助大家做相关开发。如果想看完整的接口说明,参考上面GM/T0016-2012标准即可。

在调用任何接口之前,都需要先加载所使用USBKEY设备的SKF库文件,这个文件是由KEY厂商提供,一般安装了USBKEY设备的相关软件就会有,具体文件名和存放路径要咨询厂商。加载方法与加载其他动态链接库无异:

::LoadLibrary(skfdllpath);

一、得到所有的国密证书(方法前的序号为调用顺序,下同)

根据国密标准,一种设备类型可以有多个设备(Device),每一个设备内可以有多个应用(Application),每一个应用里可以有多个容器(Container),每个容器里可以有一对证书(Certificate):签名证书和加密证书。因此,应按照下列顺序依次调用相关接口:

1.SKF_EnumDev(BOOL bPresent, LPSTR szNameList, ULONG *pulSize);

调用这个方法用来遍历当前电脑上的设备,这个方法的第一个参数一般传TRUE,表示遍历的是插上的设备;第二个参数就是返回的设备名称列表;第三个参数是设备名称列表缓冲区长度。按照惯例,这个方法应该被调用两次:第一次szNameList传NULL,pulSize返回长度;第二次给szNameList分配pulSize长度,返回设备列表,每个设备的名称以单个’\0’结束,以双’\0’表示列表的结束。

2.SKF_ConnectDev(LPSTR szName, DEVHANDLE *phDev);

通过循环调用这个方法用来连接每一个具体的设备,szName为设备名,即上面方法得到的列表中的设备名;返回phDev为设备句柄。

3.SKF_EnumApplication(DEVHANDLE hDev, LPSTR szAppNameList,ULONG *pulSize);

得到设备句柄后,再通过此方法枚举得到设备里的应用列表。hDev为连接设备时返回的设备句柄;szAppNameList返回应用名称列表;pulSize是列表缓冲区长度。这个方法也是照例要调用两次,不再赘述。同样,每个应用的名称以单个’\0’结束,以双’\0’表示列表的结束。

4.SKF_OpenApplication(DEVHANDLE hDev, LPSTR szAppName, HAPPLICATION *phApplication);

通过循环调用此方法打开应用列表里的每一个应用,hDev为连接设备时返回的设备句柄;szAppName是要打开的应用名称;phApplication为返回的应用句柄。

5.SKF_EnumContainer(IN HAPPLICATIONhApplication, OUT LPSTRszContainerNameList, OUT ULONG*pulSize)

拿到应用句柄后,我们就可以用此方法遍历应用中的所有容器了。hApplication是应用句柄;szContainerNameList是返回的容器名称列表;pulSize是列表长度;后面的就不用了多说了。

6.SKF_OpenContainer(HAPPLICATION hApplication,LPSTR szContainerName,HCONTAINER *phContainer);

循环调用此方法打开每一个容器。hApplication是应用句柄;szContainerName是要打开的容器名称;phContainer是返回的容器句柄。

7.SKF_ExportCertificate(HCONTAINER hContainer, BOOL bSignFlag, BYTE* pbCert, ULONG *pulCertLen);

最后就可以通过这个方法取得每个容器里的证书。hContainer是容器句柄;bSignFlag为导出的证书类型; TRUE表示导出的是签名证书;FALSE表示导出加密证书。pbCert为返回的证书数据,pulCertLen是证书数据的长度。同样需两次调用。

二、数字签名

在数字签名时,要指定签名所使用的证书。通过遍历本机上的证书,与签名用的证书进行对比,定位到签名证书在USBKEY中的位置,得到设备、应用和容器的句柄,然后使用证书的私钥进行签名。遍历对比的过程可参见上一节的内容。另外,由于数字签名会用到私钥,因此这里需要验证口令。

1.SKF_VerifyPIN(HAPPLICATION hApplication, ULONG ulPINType, LPSTR szPIN, ULONG *pulRetryCount);

此方法用来验证证书所在应用的PIN码,及上面说的口令,为后面的签名取得权限。hApplication是应用句柄;ulPINType是PIN类型,可以为0是管理员账户,1为普通用户,这个参数一般选择1。szPIN值是PIN码,pulRetryCount为出错后返回的重试次数。

2.SKF_ExportPublicKey(HCONTAINER hContainer, BOOL bSignFlag, BYTE* pbBlob, ULONG* pulBlobLen);

这个方法用来导出容器中的签名公钥,hContainer为证书所在容器句柄;bSignFlag 为导出密钥类型,TRUE表示导出签名公钥,FALSE表示导出加密公钥,这里选择TRUE;pbBlob为返回公钥的数据;pulBlobLen为数据的长度。这里这个方法可以不用调用两次,因为公钥结构是已知的,其长度也是固定的,因此可以直接为pbBlob分配固定长度的数据,以返回公钥。

3.SKF_DigestInit(DEVHANDLE hDev, ULONG ulAlgID, ECCPUBLICKEYBLOB *pPubKey, unsigned char *pucID, ULONG ulIDLen, HANDLE *phHash);

此方法进行杂凑(国密标准里把摘要称之为杂凑)运算初始化,并指定计算消息杂凑的算法。hDev为设备句柄;ulAlgID是杂凑算法标识,这里选择SGD_SM3(0x00000001),表明使用SM3算法;pPubKey为签名用证书公钥数据;pucID为签名者的ID值;ulIDLen是签名者的ID值的长度;phHash为返回的杂凑对象句柄。加入签名者ID值是SM2数字签名的一个重要特征,默认使用"1234567812345678"这个字符串值。

4.SKF_Digest(HANDLE hHash, BYTE *pbData, ULONG ulDataLen, BYTE *pbHashData, ULONG *pulHashLen);

初始化后,调用此方法进行数据杂凑运算。hHash是SKF_DigestInit方法返回的杂凑对象句柄; pbData为产生签名的原文,ulDataLen是原文数据的长度,pbHashData返回杂凑数据; pulHashLen返回杂凑结果的长度。同样,因为杂凑数据的长度都是固定的,这里同样可以为pbHashData事先分配固定长度,而不用再调用两遍。

注意,如果进行杂凑的数据是分组的,那就得使用下面两个方法:

SKF_DigestUpdate(HANDLE hHash, BYTE *pbData, ULONG ulDataLen);

SKF_DigestFinal(HANDLE hHash, BYTE *pHashData, ULONG *pulHashLen);

对每一组数据都使用SKF_DigestUpdate,最后调用SKF_DigestFinal返回杂凑值。当然,在数字签名运算中不存在分块计算签名的情况,所以这里也不会把数据分块杂凑。

5.SKF_ECCSignData(HCONTAINER hContainer, BYTE *pbData, ULONG ulDataLen, PECCSIGNATUREBLOB pSignature);

最后调用此方法进行数字签名。hContainer用来签名的私钥所在容器句柄,也就是遍历对比证书得到的容器句柄;pbData是被签名的数据;ulDataLen是被签名数据长度,必须小于密钥模长; pbSignature为返回的签名值。

三、验证签名

如果使用SKF库验证签名,可以按照下面的步骤:

1.SKF_CreateContainer(HAPPLICATION hApplication, LPSTR szContainerName, HCONTAINER *phContainer)

调用此方法创建一个临时容器。hApplication为容器所在的应用句柄;szContainerName是ASCII字符串,表示所建立容器的名称,最大长度不能超过64字节;phContainer是返回所建立容器的容器句柄。

2.SKF_ImportCertificate(HCONTAINER hContainer, BOOL bSignFlag, BYTE* pbCert, ULONG ulCertLen);

将签名用的证书导入到容器中。hContainer为容器句柄,即用上一方法创建的临时容器;bSignFlag为证书类型,TRUE表示签名证书,FALSE表示加密证书,这里选TRUE;pbCert,是证书数据;ulCertLen为证书数据长度;

3.SKF_ExportPublicKey(HCONTAINER hContainer, BOOL bSignFlag, BYTE* pbBlob, ULONG* pulBlobLen);

导出公钥。

4.SKF_DigestInit(DEVHANDLE hDev, ULONG ulAlgID, ECCPUBLICKEYBLOB *pPubKey, unsigned char *pucID, ULONG ulIDLen, HANDLE *phHash);

杂凑初始化。

5.SKF_Digest(HANDLE hHash, BYTE *pbData, ULONG ulDataLen, BYTE *pbHashData, ULONG *pulHashLen);

杂凑运算。

6.SKF_ECCVerify(DEVHANDLE hDev , ECCPUBLICKEYBLOB* pECCPubKeyBlob, BYTE *pbData, ULONG ulDataLen, PECCSIGNATUREBLOB pSignature);

进行签名验证。hDev是设备句柄;pECCPubKeyBlob是公钥数据结构,即第3步得到的公钥; pbData为待验证签名的数据;ulDataLen是待验证签名数据长度;pbSignature待验证的签名值。

7.SKF_DeleteContainer(HAPPLICATION hApplication, LPSTR szContainerName);

删除临时容器。hApplication为容器所在的应用句柄;szContainerName为容器名称。

可以看出,虽然基于某一种USBKEY设备SKF库可以实现验证签名,但这种方式要求系统安装SKF库并且很有可能在验证时也要插上设备(插不插设备具体要看SKF库的实现方式)。如果是数字签名操作这样要求无可厚非,但验证签名也这样就不切实际了。因为没有SKF库还可以安装,但验证签名时也要插入一个设备就说不过去了。因此,在实际应用中都是采用不依赖于设备的软实现验证方式。或者,等哪一个好心的第三方厂商开发一个基于软实现的SKF库。关于软实现的方式,已经超出了本文的范围

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值