SKF密码设备

[TOC]

概述

SKF接口是国密标准中智能密码钥匙的C语言应用开发接口标准,目前很多国内密码密码设备厂商都为其产品提供了SKF接口的开发包。开发者可以通过统一的SKF接口开发密码应用,访问来自不同设备供应商的USB-Key、TF卡、智能卡等不同形态的密码设备,而无需和某一个设备供应商的专属设备或专属接口绑定。

然而目前应用开发者开发SKF应用仍然存在一些困难和挑战。首先,SKF是一个底层的密码设备应用开发接口,这个接口不支持数字证书和SSL通信等面向高层密码应用的功能,对于应用开发者来说直接使用SKF接口开发密码应用,工作量较大。其次,由于SKF的标准草案和正式公布标准在内容上有一些差异,导致不同设备厂商的SKF实现存在一些关键性的差异,这些差异导致应用开发者难以从一个设备供应商迁移至另一个设备供应商,更难以在同一个应用中同时支持来自两个厂商的设备,无法满足典型应用场景的需求。

针对上述问题,GmSSL项目从2.0版本开始,以Engine的方式增加了对SKF设备的完善支持。GmSSL通过在libcrypto密码库中引入SKF框架,实现了对不同厂商SKF设备的支持。通过将SKF接口封装为SKF Engine,使得上层应用和SSL库可以直接通过EVP接口动态调用不同SKF设备。GmSSL项目还修改了gmssl命令行工具中所有相关功能,使得encdgstpkeyutlsm2utl等命令都可以支持SKF引擎,gmssl命令行工具还新增加了skf命令提供SKF设备的管理功能。应用开发者在通过命令行工具完成对SKF设备的初始化之后,可以直接通过SKF引擎为现有的密码应用提供SKF设备的支持,只需要提供一个SKF引擎的配置文件,而几乎无需进行任何代码开发,可以大大节省密码设备开发的工作量。除了SKF引擎之外,GmSSL项目还提供针对PCI-E密码卡和服务器密码机的SDF引擎,应用可以随意选择低成本的SKF密码设备或者高性能、高安全等级的SDF密码设备。

设备逻辑

GmSSL通过ENGINE机制支持密码硬件。鉴于《智能IC卡及智能密码钥匙密码应用接口规范》标准中公布了访问国密硬件的C语言API (下面简称SKF API),GmSSL通过ENGINE机制访问支持国密SKF API的国产密码硬件。

一个SKF API设备中可以包含一个或多个应用(Application),每个应用可以包含一个获多个容器(Container),每个容器中可以存放两对分别用于加密和签名的密钥对,以及两个相应的证书或证书链。每一个容器只能为ECC或RSA一种类型,一个容器中不能混用ECC密钥和RSA密钥。因此,在SKF API中提供公钥密码计算的接口通过Container Handle来引用公钥,容器的特性使得应用无需区分签名密钥和加密密钥,SKF API的实现会自动从容器中选择对应类型的密钥。

在使用公钥及访问容器时,应用需要进行两级认证,第一级为设备认证,第二级应用认证。设备认证需要全设备唯一的设备认证密钥,设备认证密钥是固定长度的对称密钥。访问某个具体的应用需要提供该应用的专有口令进行认证,认证的口令是可变长度的口令字符串。

管理工具

开发者在获得密码设备后,首先需要对密码设备进行初始化,如设置密码、生成密钥容器、导入密钥和证书等。目前国内的SKF设备供应商通常不提供具备这些功能的管理工具,开发者需要通过SKF编程接口自行开发所需功能。GmSSL项目通过命令行工具提供了SKF设备的管理功能,开发者可以利用该工具,编写脚本完成大规模、自动化的SKF设备管理功能。

通过gmssl命令行工具的skf命令可以访问和管理SKF设备,通过gmsslpkeypkeyutlreq等命令及SKF引擎可以密码计算、公钥导出、证书生成等功能,本节主要介绍skf命令的功能和用法。SKF接口包含设备管理、应用管理、密钥容器管理、数据对象管理等功能,skf命令封装了SKF接口的上述功能,可以完成枚举设备、创建应用和密钥容器,创建签名私钥、导入解密私钥、导入证书等功能。

由于skf命令是在GmSSL 2.4版本之后加入的,应首先确认当前的GmSSL版本支持skf命令。可以通过-help打印skf命令的所有选项,注意,随着GmSSL版本的演进,skf命令行的选项可能会发生变化。


$ gmssl skf -help
Usage: skf [options]
Valid options are:
 -help              Display this summary
 -lib val           Vendor SKF dynamic library
 -vendor val        Vendor name
 -listdevs          List installed devices
 -dev val           Device name
 -devinfo           Print device information
 -label val         Set new device label
 -transmit val      Transmit raw data packet
 -authkey val       Device authentication key in Hex
 -newauthkey val    Set new device authentication key in Hex
 -listapps          List applications
 -newapp val        Create a new application with name
 -delapp val        Delete an applicaiton by name
 -app val           Application name
 -changepass        Change application user or admin passw-phrase
 -admin             Open application as administrator
 -pass val          Application user or admin pass-phrase source
 -newpass val       Application user or admin new ass-phrase source
 -adminpass val     Application admin pass-phrase source
 -unblock           Unblock application user pass-phrase
 -listobjs          List data objects
 -importobj val     Import data object with name
 -exportobj val     Export data object by name
 -delobj val        Delete data object by name
 -obj val           Data object name
 -listcontainers    List containers
 -newcontainer val  Create container with name
 -algorithm val     Container public key algorithm - SM2 or RSA
 -delcontainer val  Delete container by name
 -container val     Container name
 -importenckey      Import encryption private key into container
 -keypass val       Private key encryption pass-phrase
 -exportsignkey     Export signing public key from container
 -exportenckey      Export encryption public key from container
 -printkeys         Print public keys in container
 -importcert        Import certificate into container
 -exportsigncert    Export signing certificate from container
 -exportenccert     Export encryption certificate from container
 -in infile         File to be imported from
 -out outfile       File to be exported to
 -inform format     Input format - DER or PEM
 -outform PEM|DER   Output format - DER or PEM
 
$ gmssl version
GmSSL 2.4.5 - OpenSSL 1.1.0d  2 Feb 2019

设备管理

设备管理包括枚举设备、打印设备信息、设置设备标签、修改设备认证密钥和发送测试报文等功能。如果需要通过skf命令访问某个厂商的设备,需要获得由厂商提供的针对该类型设备的动态库,这个动态库通常随厂商的开发SDK提供,在Linux操作系统中以so为扩展名,在Windows操作系统中以dll为扩展名,在macOS中以dylib为扩展名,在skf命令中,通过参数-lib指定厂商的SKF动态库。对于没有购买SKF硬件设备的用户,也可以通过GmSSL项目的SKF动态库libskf_dummy模拟SKF设备。关于libskf_dummy,请参考SKF Dummy

虽然SKF接口是由GM/T 0016规范定义的一个标准化接口并且给出了C语言的严格接口定义,但是不同厂商在实现这个接口时,在接口上有修改和扩展,在数据结构上也有不同的实现,因此不同厂商的SKF动态库实现和标准可能有一些差异。GmSSL为了兼容不同厂商的SKF设备,支持用户显式指定供应商,由GmSSL的SKF框架来实现不同SKF的兼容。在skf命令中,可以通过-vendor参数来显式指定动态库的供应商。

枚举设备

通过-listdevs指令可以枚举主机中已经插入的所有该供应商的设备,如下所示,其中输出的/media/guanzhi/99E1-9854为供应商SKF动态库为设备指定的设备名称。

$ ln -s /path/to/vendors/skf/libSKF.so ./libskf.so
$ gmssl skf -lib ./libskf.so -vendor wisec -listdevs
  Device 0 : /media/guanzhi/99E1-9854

打印设备信息

通过指定设备名称,通过-devinfo指令可以打印该设备的序列号、固件版本号、支持的各种密码算法以及内部存储空间等信息。由于设备信息是从指定的SKF设备中读取的,因此不同的设备在版本、支持的算法、存储空间上可能是不同的,在序列号上应一定是不同的。

$ gmssl skf -lib ./libskf.so -dev /media/guanzhi/99E1-9854 -devinfo
Device /media/guanzhi/99E1-9854 :
  Device State     : Present
  Version          : 1.0
  Manufacturer     : C*CORE
  Issuer           : XXXX
  Label            : MyDevb
  Serial Number    : 34:32:33:33:35:38:33:38:33:37:30:33:35:36:36:32:46:46:46:46:46:46:46:46
  Firmware Version : 1.0
  Ciphers          : sm1-ecb,sm1-cbc,cbcmac-sm1,ssf33-ecb,ssf33-cbc,cbcmac-ssf33,sms4-ecb,sms4-cbc,cbcmac-sms4
  Public Keys      : rsa,rsaEncryption,sm2sign,sm2exchange,sm2encrypt
  Digests          : sm3,sha1,sha256
  Auth Cipher      : sm1-ecb
  Total Sapce      : 115680
  Free Space       : 109920
  MAX ECC Input    : 256
  MAX Cipher Input : 23520

设定设备标签

除了通过设备序列号来区别SKF设备,用户还可以为SKF设备设定一个可打印字符串类型的设备标签。和唯一的序列号不同,用户可以为设备标签设定为任意的字符串,也可以给多个设备设置相同的标签,因此不应将设备标签作为设备唯一标识符。

$ gmssl skf -lib ./libskf.so -dev /media/guanzhi/99E1-9854 -label MyDev

在标签设备完成之后,可以通过打印设备信息来检查是否设定成功。

发送测试报文

SKF设备提供了一个底层的测试接口,用户可以通过这个接口向设备发送原始报文数据,例如对于兼容智能卡的SKF设备这个原始报文可能是APDU报文。这个底层接口可以用于功能测试或者提供一些不公开的功能,skf命令通过-tranmit指定封装了SKF接口的这一功能。

$ gmssl skf -lib $LIB -dev $DEV -transmit $APDU_HEX

更换设备认证密钥

在进行应用管理时,应该提供设备的16字节认证密钥完成设备操作。设备的认证密钥是16字节的对称密钥,在skf命令中,需要以16进制字符串编码的方式提供设备的认证密钥。

设备认证密钥通常在设备出厂时有预置的默认值,用户在拿到设备后,应该理科更换设备的认证密钥。指定-newauthkey用于修改设备认证密钥。注意,skf命令以16进制编码的方式读取设备认证密钥,从安全角度出发,不应该将设备认证密钥的字符集局限于数字或者可打印字符集的范围之内。

$ export AUTHKEY=00112233445566770011223344556677
$ gmssl skf -lib ./libskf.so -dev $DEV -authkey $AUTHKEY -newauthkey $AUTHKEY

应用管理

应用管理包含创建应用、枚举应用、删除应用。首先,我们可以通过-listapps指令枚举指定设备中的应用,普通用户在拿到设备时,可能已经有预先设备好的应用。

枚举应用

下面的例子展示了枚举应用,可以看到指定的设备中共建立了两个应用,名字分别为DEFAULTMyApp1。在SKF中,应用是通过PIN保护的,用户必须提供正确的PIN才能够进一步访问应用的内部数据和功能。应用支持普通用户和管理员用户,分别有不同的PIN。枚举应用可以看到这两个用户的PIN重试次数。

$ gmssl skf -lib ./libskf.so -dev $DEV -authkey $AUTHKEY -listapps
Application 0:
  ApplicationName  : DEFAULT
  AdminPinMaxRetry : 6
  AdminPinMinRetry : 6
  AdminDefaultPin  : True
  UserPinMaxRetry  : 6
  UserPinMinRetry  : 0
  UserDefaultPin   : True

Application 1:
  ApplicationName  : MyApp1
  AdminPinMaxRetry : 6
  AdminPinMinRetry : 6
  AdminDefaultPin  : True
  UserPinMaxRetry  : 6
  UserPinMinRetry  : 6
  UserDefaultPin   : True

创建应用

通过-newapp可以创建新的应用,在创建应用时可以通过-pass-adminpass分别指定普通用户的PIN和管理员用户的PIN,注意这两个选项支持标准的口令输入格式。

$ gmssl skf -lib ./libskf.so -dev $dev -authkey $authkey -newapp MyApp -pass pass:123456 -adminpass pass:P@ssw0rd

删除应用

SKF接口在设计上支持一个设备中可以创建多个应用(Application),但是设备的厂商实现上可能只支持创建少数应用或者只支持创建仅一个应用,在这种情况下,如果设备中已经创建了应用,那么我们需先将已有的应用删除。

通过-delapp指令可以删除一个应用以及应用的所有密钥容器。由于删除应用(以及密钥容器内的私钥)是不可逆的操作,因此在执行这个操作必须小心。

$ gmssl skf -lib ./libskf.so -dev $dev -authkey $authkey -delapp MyApp

修改口令

下面的例子分别完成修改普通用户PIN和管理员用户PIN。

$ gmssl skf -lib $lib -dev $dev -authkey $authkey -app MyApp -changepass -pass $OLD_PASS -newpass $NEW_PASS
$ gmssl skf -lib $lib -dev $dev -authkey $authkey -app MyApp -changepass -adminpass $OLD_PASS -newpass $NEW_PASS

解锁用户口令

在SKF设备中,如果输入连续错误6次,那么口令会被锁定。通过-unblock指令可以用管理员口令解锁已经被锁定的普通用户口令。但是如果管理员口令被锁定了,那么是无法通过SKF命令行工具解锁管理员口令的,只能联系设备的生产厂商解决。

$ gmssl skf -lib $lib -dev $dev -authkey $authkey -app MyApp -unblock -adminpass $OLD_PASS -newpass $NEW_PASS

密钥容器管理

SKF设备中以密钥容器的方式管理私钥,一个应用中可以建立多个密钥容器,每个密钥容器中可以装载同类型的2对公私钥对,其中一个为签名密钥对,一个为加解密或密钥交换密钥对。签名密钥对只能在SKF设备内部生成而无法由外部导入,而加解密或密钥交换密钥只能从外部导入而无法在设备内部生成。在完成密钥生成或导入后,密钥对中只有公钥可以导出,私钥是无法导出的。密钥容器还支持证书的存储,一个密钥容器中可以存储两个证书或两条证书链,分别对应两个密钥对。

通过skf命令可以完成密钥容器的枚举、创建和删除。

枚举密钥容器

$ gmssl -listcontainers

创建密钥容器

下面的例子给出如何创建一个密钥容器的过程。在创建密钥容器时需要指定密钥容器密钥的类型,例子中指定生成SM2类型的密钥容器,如果需要生成RSA类型的密钥容器,则应该采用-algorithm RSA参数。在创建密钥容器的过程中,-newcontainer指令会自动在密钥容器中生成签名私钥。

$ gmssl skf -lib $LIB -dev $DEV -authkey $AUTHKEY -app MyApp -newcontainer sm2 -algorithm SM2
$ gmssl skf -lib $LIB -dev $DEV -authkey $AUTHKEY -app MyApp -importenckey -in sm2.pem

下面的命令打印名字为sm2的密钥容器中的所有密钥(公钥)。

$ gmssl skf -lib $LIB -dev $DEV -authkey $KEY -app MyApp -container sm2 -printkeys

删除密钥容器

下面给出删除密钥容器的例子:

$ gmssl skf -lib $LIB -dev $DEV -authkey $KEY -app MyApp -delcontainer sm2

导入证书

通过指令-importcert可以向密钥容器中导入证书,一个密钥容器支持存储签名证书和解密/密钥交换两个证书(或证书链),skf命令会通过检查证书中的密钥用途是否为签名来决定将导入的证书存储到哪个位置。注意,在导入证书前应检查证书的公钥类型和目标密钥容器的公钥类型是否一致,而且新导入的证书会覆盖容器内的原有证书。

$ gmssl skf -lib $LIB -dev $DEV -authkey $KEY -app MyApp -container sm2 -importcert -in cert.pem

导出证书

由于一个密钥容器中包含签名证书和解密/密钥交换两个证书(或证书链),因此skf命令提供了两个指令用于导出不同用途的证书。

$ gmssl skf -lib $LIB -dev $DEV -authkey $KEY -app MyApp -container sm2 -exportsigncert -out signcert.pem
$ gmssl skf -lib $LIB -dev $DEV -authkey $KEY -app MyApp -container sm2 -exportenccert -out enccert.pem

数据对象管理

SKF应用支持存储若干数据对象,由于应用是受到PIN保护的,因此用户程序可以将需要保护的用户数据、配置文件等安全敏感数据以数据对象的方式保存到SKF设备中。但是由于SKF设备的内部存储空间较小,通常只有几十KB到数百KB,因此只能存储较少数据。通过skf命令可以实现数据对象的枚举、导入、导出和删除,其中skf命令只支持以对象为单位的导入和导出,如果用户需要对数据对象做更细粒度的读写,那么可以将一个数据对象分解为多个存储,或者直接通过SKF的C语言接口编程访问。

枚举数据对象

$ gmssl skf -lib $LIB -dev $DEV -authkey $KEY -app MyApp -listobjs

导入数据对象

指令-importobj支持将主机上的一个文件以数据对象的方式导入到一个应用中,并为该数据对象指定一个名字作为该对象的标识符,注意在一个应用中数据对象名是唯一的。

$ gmssl skf -lib $LIB -dev $DEV -authkey $KEY -app MyApp -importobj MyObj -in ./MyObj.bin

导出数据对象

通过-exportobj可以将一个数据对象从设备中导出。

$ gmssl skf -lib $LIB -dev $DEV -authkey $KEY -app MyApp -exportobj MyObj

删除数据对象

$ gmssl skf -lib $LIB -dev $DEV -authkey $KEY -app MyApp -delobj MyObj

SKF引擎

在GmSSL系统中调用SKF设备密码功能是通过SKF引擎完成的。引擎机制是GmSSL中用来对密码设备功能进行抽象的一个内部接口,一个密码设备被抽象为一个Engine对象,并通过EVP接口为应用程序提供编程接口。GmSSL将SKF接口封装为一个SKF引擎,因此所有的上层应用都可以通过EVP接口调用SKF设备的功能,命令行工具gmssl的密码功能也都支持引擎。

编译和安装

由于SKF引擎默认不编译,因此从源代码安装SKF引擎需要在配置中显式启用SKF引擎。如果没有指定静态编译和安装路径,编译好的SKF引擎会作为一个动态库安装到/usr/local/lib/engines-1.1/路径,SKF引擎的动态库通常名为skf.soskf.dllskf.dylib

$ ./config enable-skfeng; make; sudo make install

SKF引擎的动态库实际上只完成了将SKF接口封装为Engine对象,因此要将SKF引擎动态库如skf.so和设备厂商提供的libskf.so区别开来,并且SKF引擎动态库是依赖厂商的动态库才能访问SKF设备的。

如果SKF引擎是通过二进制分发获得的,可以直接将SKF引擎的动态库手动拷贝至/usr/local/lib/engines-1.1/路径。

获得设备信息

完成安装后,可以通过engine命令检查SKF引擎的命令和支持的算法:

$ gmssl engine skf -vvvv
Using configuration from /usr/local/ssl/openssl.cnf
(skf) GmSSL SKF engine 1.1.0
     SO_PATH: Specifies the path to the vendor 'skf' shared library
          (input flags): STRING
     VENDOR: Specifiies the device vendor
          (input flags): STRING
     VERBOSE: Set verbose, 0 = quiet, 1 = verbose
          (input flags): NUMERIC
     AUTHKEY: Specify device authenticate key
          (input flags): STRING
     DEV: Specify device to be opened
          (input flags): STRING
     APP: Specify application to be opened
          (input flags): STRING
     CONTAINER: Specify container to be opened
          (input flags): STRING

通过engine命令中的-pre选项可以执行这个SKF引擎内置的命令。

通过engine-c指令可以打印SKF设备支持的密码算法,在不打开SKF设备的时候,该指令只能够显示SKF引擎支持的密码算法。

$ gmssl engine skf -c
Using configuration from /usr/local/ssl/openssl.cnf
(skf) GmSSL SKF engine 1.1.0
 [RSA, EC, RAND, id-ecPublicKey]

下面的例子中我们通过-pre指令打开设备,这样SKF Engine能够从当前的设备中获得该设备支持密码算法的详细信息。

$ gmssl engine skf -pre SO_PATH:./libskf.so -pre AUTHKEY:432a434f524520535953204020535a20 -pre DEV:/media/guanzhi/99E1-9854 -c
Using configuration from /usr/local/ssl/openssl.cnf
(skf) GmSSL SKF engine 1.1.0
[Success]: SO_PATH:/home/guanzhi/code/GmSSL-master/engines/skf/ccore/lib/linux/libSKF_sd_x86_64_1.6.17.0912.so
[Success]: AUTHKEY:432a434f524520535953204020535a20
[Success]: DEV:/media/guanzhi/99E1-9854
 [RSA, EC, RAND, SM1-ECB, SM1-CBC, SM1-CFB1, SM1-CFB8, SM1-CFB, SM1-OFB, SMS4-ECB, SMS4-CBC, SMS4-CFB1, SMS4-CFB8, SMS4-CFB, SMS4-OFB, SSF33-ECB, SSF33-CBC, SSF33-CFB1, SSF33-CFB8, SSF33-CFB, SSF33-OFB, SM3, SHA1, SHA256, id-ecPublicKey]

配置文件

当使用engine之外的命令行指令时,如pkey等,这些是不支持engine-pre指令的,因此我们无法输入上述SKF命令。但是在命令调用SKF设备之前,必须执行这些命令来打开设备和应用。命令行工具gmssl中可以访问Engine的命令都支持-config参数,可以通过制定一个配置文件来指定访问SKF引擎的密码功能前执行哪些操作。一个典型的SKF引擎配置文件如下所示:

# conf file for gmssl skf engine
openssl_conf = openssl_init

[openssl_init]
engines = engine_section

[engine_section]
skf = skf_section

[skf_section]
engine_id = skf
VERBOSE = 1
SO_PATH = /path/to/libskf.so
AUTHKEY = 31323334353637383132333435363738
DEV = /media/guanzhi/Factory
APP = MyApp
init = 1

这个配置文件中的主要内容是提供厂商SKF动态库的路径、认证密钥、待打开的设备和应用等信息。可以将上述内容写入到skf.cnf配置文件中。

密码计算

调用SKF引擎进行SM2签名

使用容器中的签名密钥签名

echo "abc" | gmssl sm3 -binary | gmssl pkeyutl -sign -pkeyopt ec_scheme:sm2 -engine skf -config skf.cnf -keyform engine -inkey sm2.sign -out sm2.sig

使用容器中的签名密钥验签

echo "abc" | gmssl sm3 -binary | gmssl pkeyutl -verify -pkeyopt ec_scheme:sm2 -engine skf -config skf.cnf -keyform engine -inkey sm2.sign -sigfile sm2.sig

使用导出的签名公钥验签

$ gmssl pkey -engine skf -config skf.cnf -inform engine -in sm2.sign -pubout -out sm2vkey.pem
echo "abc" | gmssl sm3 -binary | gmssl pkeyutl -verify -pkeyopt ec_scheme:sm2 -pubin -inkey sm2vkey.pem -sigfile sm2.sig

调用SKF引擎进行SM2解密

用SKF引擎实现加密和解密

echo "Top Secret" | gmssl pkeyutl -encrypt -pkeyopt ec_scheme:sm2 -engine skf -config skf.cnf -keyform engine -inkey sm2.exch -out ciphertext.sm2
gmssl pkeyutl -decrypt -pkeyopt ec_scheme:sm2 -engine skf -config skf.cnf -keyform engine -inkey sm2.exch -in ciphertext.sm2

从SKF引擎中导出公钥加密,用SKF引擎解密

gmssl pkey -engine skf -config skf.cnf -inform engine -in sm2.exch -pubout -out sm2ekey.pem
echo "Top Secret" | gmssl pkeyutl -encrypt -pkeyopt ec_scheme:sm2 -pkeyopt ec_encrypt_param:sm3 -pubin -inkey sm2ekey.pem -out ciphertext.sm2
gmssl pkeyutl -decrypt -pkeyopt ec_scheme:sm2 -engine skf -config skf.cnf -keyform engine -inkey sm2.exch -in ciphertext.sm2

调用SKF引擎计算SM3摘要

$ echo -n "abc" | gmssl sm3 -engine skf -config skf.cnf -engine_impl
(stdin)= 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0

调用SKF引擎进行SM4加密

$ echo -n "Message to be encrypted" | gmssl sms4 -K 12345678123456781234567812345678 -iv 12345678123456781234567812345678 -a
E36cJoAudKNjIN/W2Pu86cFoN6f2OL0mMV39Zlq6xpk=

调用SKF引擎生成随机数

$ gmssl rand -engine skf -config skf.cnf -hex 16
16ef95ca891d7b64595f7309a3175318

签发证书

SKF设备通常作为客户端私钥和证书的载体。对于解密/密钥交换私钥,由于私钥是在设备外生成的,可以采用常规的方式为密钥对申请证书,并将私钥导入到SKF设备中。但是由于签名私钥无法导出,只能通过调用SKF Engine生成密钥容器中的签名私钥对应的证书签名请求(CSR)文件,然后将CSR文件提交至CA并签发证书。

下面给出一个通过req命令生成SM2密钥容器的证书签名请求文件的例子,注意常规req命令需要提供一个私钥文件,而在这个例子中,通过SKF引擎指定的密钥容器代替了常规的私钥文件,密钥的名字sm2.sign表明采用名为sm2的密钥容器中的签名私钥,载入SKF引擎所需的配置信息从配置文件skf.cnf中读取。

$ gmssl req -new -sm3 -subj "/C=CN/ST=BJ/L=BJ/O=PKU/OU=Sign/CN=Alice" -keyform engine -keygen_engine skf -engine skf -key sm2.sign -config skf.cnf -out csr.pem

由于密钥容器受到应用PIN的保护,因此在这个命令执行的过程中会要求用户输入应用普通用户PIN。

测试用动态库

由于我们目前没有可以用于测试的在Linux/Mac上支持SKF API的硬件设备,因此GmSSL包含了一个SKF API的软件Dummy实现用于完成编译。GmSSL还提供了一个独立的测试程序,用于测试目标硬件是否满足GmSSL的规范和功能要求。

C语言接口

首先,虽然GmSSL中包含SKF的C语言接口,但是我们不建议应用开发者通过这些接口编程。通过GmSSL原生的EVP接口(以及ENGINE对象),应用开发者可以使用一套标准的密码接口开发应用,通过替换SKF引擎、SDF引擎、GMI引擎等,灵活选择密码功能的软件实现或硬件实现,而无需和某个类型的密码硬件或者某个特定厂商的密码硬件绑定。

GmSSL中SKF的C语言接口包含以下几个头文件:

#include <openssl/skf.h>
#include <openssl/sgd.h>
#include <openssl/gmskf.h>
#include <openssl/gmapi.h>
#include <openssl/is_gmssl.h>

其中skf.h包含了SKF标准中的C函数和数据结构,sgd.h包含了一些常量的定义,这两个头文件及相关接口的详细文档可以参考《GM/T 0016》。SKF设备的生产厂商通常也随着开发包提供了这两个头文件(可能用了不同的名字),GmSSL项目提供的SKF头文件是严格遵从正式标准的。为了便于开发、测试以及与GmSSL的其他组件交互,gmskf.h封装了一组辅助函数,例如打印某些数据结构和GmSSL内置类型的导入导出,gmapi.h提供了在SKF数据类型和GmSSL内置数据类型之间进行转换的辅助函数。

GmSSL对设备认证规范做如下规定:

  1. 设备认证密钥固定为16字节
  2. 设备认证密码算法固定为SMS4 (SGD_SM4_ECB)。
  3. 设备发出的挑战数据是在调用SKF_DevAuth()之前,最后一次通过SKF_GenRandom()获取的16字节随机输出。
  4. 设备的响应数据为采用SMS4加密加密该16字节随机值后的密文

GmSSL对需要进行设备认证的场景做如下规定:

  1. SKF API的实现必须支持SKF_SetSymmKey()
  2. SKF_SetSymmKey()SKF_EncryptInit/Update/Final()等对称加密功能无需设备认证和应用认证
  3. 访问容器时需要进行两级认证

140716432576448:error:1010A0B0:elliptic curve routines:pkey_ec_paramgen:no parameters set:crypto/ec/ec_pmeth.c:615:

140716432576448:error:141A4006:SSL routines:tls_process_ske_ecdhe:EVP lib:ssl/statem/statem_clnt.c:1647: