USBKey Learning

 

证书

一些证书类型:

  • PEM:既可以保存证书,也可以保存密钥(为了区别证书和密钥,将后者后缀改成key

  • CER,CRT:只保存证书,不保存密钥

  • P12(PFK):同时保存证书和密钥,有密码保护

所谓的用户证书,就是用户用自己的私钥对对自身信息内容进行签名,然后发给CA证书签发机构,CA验证后用CA的私钥签发的数字证书

此外还要知道的文件类型

  • CSR:证书请求文件

使用openssl生成p12格式的RSA和ECC证书

  • 生成n位RSA密钥

    1
    $ openssl genrsa -out 4xwi11_rsa_sk.key [n]
  • 从RSA密钥中提取公钥

    1
    $ openssl rsa -in 4xwi11_rsa_sk.key -pubout -out 4xwi11_rsa_pk.key
  • 在prime256v1下生成ECC密钥(前面的参数是椭圆曲线的参数用openssl ecparam -list_curves命令查看

    1
    $ openssl ecparam -genkey -name prime256v1 -param_enc explicit -outform pem -out 4xwi11_ec_sk.key
  • 从ECC密钥中提取公钥

    1
    $ openssl ec -in 4xwi11_ec_sk.key -pubout -out 4xwi11_ec_pk.key

    ECC查看私钥内容

    1
    $ openssl ecparam -in 4xwi11_ec_sk.key -text
  • 用密钥生成证书

    生成证书申请文件

    1
    $ openssl req -new -out 4xwi11.csr -key 4xwi11_ec_sk.key -keyform PEM

    签发crt证书(这里其实是自签名

    1
    $ openssl x509 -req -in 4xwi11.csr -out 4xwi11.crt -signkey 4xwi11_ec_sk.key -CAcreateserial -days 3650

    将crt转为p12

    1
    $ openssl pkcs12 -export -clcerts -in 4xwi11.crt -inkey 4xwi11_ec_sk.key -out 4xwi11.p12

    使用下面命令可以查看私钥

    1
    $ openssl pkcs12 -info -in 4xwi11.p12 -nodes -nocerts

使用gmssl生成p12格式的SM2证书

安装gmssl,参考https://github.com/guanzhi/GmSSL/tree/master

为了加深理解,在用gmssl生成SM2证书的同时,模拟下证书是如何签发的

首先要关注下gmssl的配置文件/usr/local/gmssl/openssl.cnf(在安装gmssl的目录,因为要符合配置文件,所以要保持文件名相同或者根据需求进行修改,添加相应的默认文件,即mkdir newcerts private certs crl && touch index.txt && vi serial,并在serial中输入初始化序列号,比如02

其中

  • dir:CA所在的目录
  • certificate:CA的证书
  • private_key:CA的密钥

image-20220413161356161

  1. CA生成密钥

    1
    $ gmssl sm2 -genkey -out private/cakey.pem
  2. CA自签名证书

    1
    $ gmssl req -new -x509 -key private/cakey.pem -out cacert.pem
  3. 客户端生成密钥

    1
    $ gmssl sm2 -genkey -out 4xwi11.key
  4. 客户生成csr(好像公司等信息要和CA自签时一致

    1
    $ gmssl req -new -key 4xwi11.key -out 4xwi11.csr
  5. CA签发crt证书

    1
    $ gmssl ca -in 4xwi11.csr -out 4xwi11.crt -cert demoCA/cacert.pem -keyfile demoCA/private/cakey.pem
  6. 验证签名

    1
    $ gmssl verify -verbose -x509_strict -CAfile demoCA/cacert.pem 4xwi11.crt

    image-20220413162645103

  7. 将crt转为p12

    1
    $ gmssl pkcs12 -export -in 4xwi11.crt -inkey 4xwi11.key -out 4xwi11.p12

将导入usbkey的容器里,接着就是很迷的地方,如下图,上面那个是老师帮我用在线网站生成证书然后导出公钥和私钥的,下面那个是我自己的, 我是没找到有什么办法可以把公私钥像上面那样导出来,反正我的无法正常使用(只能对称加密。其中应该有版本的问题,我看别人的都是V2.2.19,直接导入就是上面那样,而且还有创建容器的按钮,WTF

image-20220413164549289

不过这个问题是可以通过调用API来解决的,后面还会提到

运行demo

总之折腾到这里本型号demo提供的功能都能够使用了

image-20220413170153545

image-20220415122507534

调用智能密码钥匙实现以下功能

在所有的操作之前,先实例化mTokenPlugin对象

1
var token = new mTokenPlugin();

获取随机数

函数原型如下

image-20220331140303276

1
2
3
4
p = token.SOF_GenerateRandom(100)

console.log(p);
console.log(_Base64decode(p));

获取的随机数base64解码出来是字节码,如下图所示。和预想的不一样。。。所以硬件是产生的随机值是字节形式的

经过验证uLength是随机数的字节长度,怎么验证的呢。。有点啰嗦


对这些字节码进行转10进制数字操作,代码如下,结果如下图二所示:20个字节(256进制)转10进制bit位在160左右

进一步分析随机函数参数uLength与bit位的关系,代码如下,结果如下图三所示,可以说是SOF_GenerateRandom函数接口会生成uLength*8bit长度的随机数,即uLength表示随机数的字节长度,并且可以传浮点数

image-20220331113253307

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from base64 import b64decode

# u1Length = 20

a = ['TFqASTnk/wbgnJdrsrhTm/f3TOM=',
'48mhqXP2TVvDEobwtrnu0ygRETI=',
'Ic1j/mrYsVf1nrHJYU5pvjk8T2A=',
'JhYXyMjulyA2S4bW147VhnnRzy8=',
'DtyF8fKEmxdHAZnMj0/ySy/DNqI=',
'w3+204ZtTNg+0bwjJx2raPBKMds=',
'6pv9XIWX1FOHrMb1pArMZEHGltI=',
'fadDSTrjvzx31ZyObwgMmOCz4mw=',
'rGS3q41jnt7NiOjymaMAOkcd89U=']
for i in a:
val = 0
src = b64decode(i)
assert len(src) == 20
for j in range(len(src)):
val += src[j] * 256 ** (len(src) - j - 1)
print(f"256进制 ====> {val} && {val.bit_length()}nbits")
print(f"l2b ====> {int(src.hex(), 16)} && {int(src.hex(), 16).bit_length()}nbits")
print()

image-20220331130401022

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
from base64 import b64decode

a = '''ag==
hI8=
0Sm+
CZkaAQ==
D0Dx6mY=
068tI/hc
ncwlaByQDA==
75LErcGq05M=
wjZVgpFGfT9T
L+WBOYEtterUrA==
jbhw/b4YoYpR+Ec=
Nk/dDpr8EOx9iSeS
r0hXkHskSHLQ+UMQ7A==
eeK12gh+wg+Xhk9eVLU=
UJ1xtQt/BjO7h8mn7z67
HUAO119KLqthbpX1zgvZcA==
quI3zQp4rc0WOSnXZpfTV74=
MPWaDcS1IRBpzwvX8hZGvQI/
TS16Gp6tsPKvebTacsQS6ZmYsQ==
1GKgYDJKoW8IJ9HaW/ORecc3xQc=
f4BigtV4opSqbABIwMUfMTBYMI96
UOaNdWFj8imAu67qu/iRSIcRIuGS1g==
lcfCepOg/mve2G08xRYKOOTTL+6zkFo=
MRcKTt8yjdqkCHbX9+4O+6O3xzSEjQDj
ES3f7/RSfO0aolrMPAzt+MIQhaNH7CkbFA==
lA897tf2Gs9mPLHIWZ+w6w6r0T4W4fMHfLQ=
NRTOxPqLWCfiXju8C+Dx5f3xTZSmgYSbGd7B
DLHWBO289HTYHvyK+Bm1tcrqtP008zL5QsJsBg==
1R0MRh31HJDnM0XQqKgidxiLflRnrpLme9b1tRE=
FCgYN14B2ZMrl0Msi6gJe87BuZRRpeqhu0d4y2T3
qN9sPcdG2b0Lt99t1NYwmXpLc6WLe9GpPO8XsMr82Q==
sWIr4C+6HqFo5CEiMvnk5Mqqr3Pmx35beLTSAveS/UU=
r6xdBYpVZexlUgqDh1FsDi+DDhqyrDksLFr9ilLVjtjf
5ps8iGmx3shHAPefTU/5AhIGMuu7njIakNFywcJIULZZ1A==
3BopWTfTO4N+x5v+yHjscolXWkwOqBdI4yPeMGefRhmp3wI=
Ip2cZ5A1UTFrFsxIakG9DUPATXqbS0KSMu+aqW+sBhqeTpPe
M5PxkVw1NPK9OyfIScu4JbSA0xfjxuwqjl0+TTgG7c7ohLZFcw==
VdSryos9SkzYDp/9FJ9CrgiCSGDO82Yk70mIKIVGOx84e43FVrQ=
2I+cngj0i8R8De++1saob3CEzOA4QYwC2efYXrKNheiaBGmXdsit
RGrDRgwEjpyLbywpW8hddYRpAsVxTcthPOTYxxOtNPOnwG3kcbQwsA==
xkqdiU0j/sxqHZS/1pzps5hdPjcO7qIkC8HjBIJGd5DT140lulD0u6A=
bBz9giAeit7NNaIC6+miB4H+kRCm2cV4p02Uxgp/BdqH5XkNKEEXvSjK
fe2MPtPIk9uBwOEA0tjrtfF2cDJbqrXZ8TMLu7rXva1+LwTNwn7S80Qtbw==
DqLOCXrkhiKm698fYJ2hjQ2U+U2cMzZM8zJbldvE5U/31/OK+00KhXuAvDw=
NZSASmxK054Vkepw3RHDzN1ktv8ws/6ygdgNrjesqN5Dvcy0htCajU3MavMu
cCO05rf7qSyJYqKtwW0hfezE/3uvJdXZcpJoLqkL2WCWevLz0gPkaQ9FkLFtMQ==
sFfqBBsD50+GHzkDYM26NAkXWclJbrpdsvYvxVZobhnGXVO71XOvZPDJBzt9cxw=
e7ZNpcp8absdDM1Rr6w9HE1mJ6HiucrrGaEsWGRl0TvQkXXrv+i2BiWB6Am/PfB2
czOPxtLV7/x0n4Qckbs3jzlFWaQGw7S+Oi/v5e7QEPLFpml2hTmf+/UvgyZFMosxxQ==
j2kjfvLVbHyNwcys3QuLDNGRwpFRiQvJy++1qUkhVvAwUUTib/zhP1JuqRfhYbH1muo=
xMdZnrCht3I2Ly9QtpmEC+FxSPgYj7PrVF3sC9oYleNXaYITAWzgYvtaNpFIb+CJaz7u
lpbl2niIqCKRN+BcJ++AEtUkyYo5oUsTisLLQYGIXaRyT2mv+4ld8W5eloD6J/RuUE/o1A==
HwJCb2T1dOIQlJp5Jy1dTrrKk8hqf+fgCqYyEOk0GhtA6lZUMa1bYCu/yzJl/cz2ppt/wyk=
lhut0vBBSSEVC1wxvwTzCTzwcWVOiwoovdafUuG8UKOMWjTjEqQot92o2V01HCWGlwZyb0cG
21hBqIC4GdIL+Hs80pu7FYmefeQ1bBiSb9DpAUljYn7EtJAxFIxWwhP65OIsImc2+h6WimEgsg==
PDH4YNMJ7SZ3UkXljr2kKTH8VvJ8HyOCK3Na92c2KVbnOJKQmMMfymBjCmbRlX3CrIUSHFZ+8Ao=
75T5YhJ6iPRN+EGcq/IEv1sIkgM0uj0MFHaN4xsKFRWOkA9lgDSM8HHDhZe0QxgYrARWcLuzhFHP
QC8zKJ4x6hXExqGD1aJgxZwzMefaKzw7GnWz4VG5HabW41XcLDdCy/BGRhdIEgezhu3I9LYdqnOziA==
zMwi+jrILWmp761Kqu5I+sSbjI6a7Aq0ZTINN8M2YZTrxrlBdkOoqhMNeZRyOnHHXcTX0ApJYi9GPfw=
c+kVJ8Jkl2R23E7nz/nhVrpN88UFcOGcrVShCa9KZUbIb8B5Vp55gS03zGjFnJ2pdwWiZI8hOnMDgeYQ
gF5V7dY/gjknFnampP2sgS2OGPtzxbOmhD/gA/CUWIBTARS5aZIOH1ucL9TTw9cy7pASDp5c+OrGxt5/0A==
rgpYBo/iU7y0eekZ9P+V5hpw/niSO/ZiFNSBpbmnRmG3zDV54dK/94QSiG93/iiWrPf1wHwAK+xgSnCh3Gw=
lFo+c8sAML/iUfOtdr2m/2pYr3bAqXfHVjbct1kps8sjGUNzpizxo380sv5vGbWe6wcN61ve4g6QYkETQdKs
WsIoM7O0o0Y/q7gVY9kzfg1jpwGXugmvMKdZFBfgrUawDoo9Gj6n2uxmiiGzSfBgdUv9mB0vDB2HnWNdnox6Vw==
kuThcoRn1SaOa8WtygLWEBaEcJWpy965qonIkAAYc3RFkwv02OXKdLR3r177GL7hbRmBf/e1JoMV7BwkVU5QM+g=
p1xkYFnMzfdc82rl7+ap3Hcg/JPaBgQWBfsxxVNHC8O0LhULZRy4R9Mchcayz34P9kZXGC1wJA2A98F+5NJI7bS2
c45NRpfx7wwFwupCnNNBsGJFdDHT+rp2mNJPbKxwIjs+j4oVIxlcVYLLOZEllvhUxCziafDspDGbyoPmgkntE5qo4g==
zhH2LRT0/IqIVjbCssBSna84TvHfFCbUpih5ONYFBl06hCbvmgd8jE6aaHu+/1ahGFgSji7uHcaO8XIOKtlYk9HnRpA=
//5V/ePt5Ryc7RK/k6Y7VE0IcufLav+UNeUvaEMZixhGGnvndeL+mfMfBy7xi4gAgjwtDS4LMakEleOamZhGplKCCqa2
YNVJ6yNFMKku+4e6CtibSn8yvFIV8TCzpXt6xdgdXypSlXbcZwzKa6Yp+vMKGDwcLAmimXCvc5RbzpRTK7ycMja4Xy1WzA==
eTnppoAMfCwcDKI5Y5bMaWYIXPTliPVInt5eAzbXOyGA/1ilR5VzMyFs0RDdk35TxIV6gOqp+kI0y7f9VjhofmZ3xW9iS14=
7fzG82S4iFkTF67rTIZXnq1OzcurS1E3BLiHNQ6OpZX6cBHwQ3pyRh8+4NxX/Fan55rRu4T3sueNYkkyALbL+ocsY2WqzCCN
+FgoYx5RKEVC56nIlonC2KakkJNDKZC+JXlMIzYg/J1JQoqZhzSzw9qtxfOoqH35+VvyHtBYqn85tgDxQ4H3/C30TLOA9TGx2Q==
E1wah2OhOC4Ci69pSfC8xgHKQ2xsKPrOAcSqqW2iHrU2U4U41YftwKmP9ecaV1ppckzJTNWgPUnMXWT9qW36aJfLXr8HldeQMkk=
M7tJwW8Uy7ZTnwTsMUghgUAZf/8s7zO7WlJvfRdb6TyMhgQ/DQXUNYE2f5kxs5K2GOjMBpY0l9eTG2fsvqx0GyYZUh0vmwBXRtfl
sT7xNdQA5a8E4CknveaEaes2nqIAWGFVEO59Eqsuj9mn+ORTQPK7bviPNDnuVLw1WGPGLUbDodXRO4aR+CTNykf48SFqE30Q76iF4w==
/y/Vhv9MLDoU7I/OUNPe3owl6NqIDn+WsOh+y9brBybBX3AiIKXafTu+A/ldZzTGur2UFmBxHKGnfc16e0D9gSwxchRCbEP83krbYu8=
kLm+qmyPUVIDC6meodWE0HJdp0EjiwZVFRlRic6SUT+6aOOetWS3ssyp+wyh8XNp7gmUUrwUhWyDCVPMwATLm0V995SOCcO3cu0UXigK
Cgms7yCoIzF1GduQBzFojjOh0KW/nbb5Y96PRYjn9DlbPbAA85XRUMMhfnZMQjaCQvCN3BTpI1g0yPQR/spd87e30DhYm1tZv1Q47ipMOw==
5e25+gtBAiEFsS0/79Ng04moXbV1ZCUhgTtuXrV1pbDyzjRZrqiNTYOrv0mh1lJa1dEtfWuU8z584yz2JwF8y8oRA60oIWpZuq7hjCa8PHQ=
wUWEuc+rWRpto1IuUXX78V+dVnUcXyPRwQ9+rZkcolY5GsDD+qIIVOLeZniKIW2YCrDcFYeurhgnZ0i8hoODpDb4072Gt27jnksutrU8hSD/
9yOJv/swvwBq/4elnXW5TqtENuB5P7jnp0RsIGeO3kzXutM7ogjnLOUbFJNSv1a3RFxHK1GejNahxZzSgmK6gFbdXsiYliuC7bQUMDDzZ36TdA==
+vA3YDDLZKObVe9sMpdypWBGtrVNNCOiS7uH/sFDFzmjaUD+kcfnDnVizt1GQiZAi3qZgxw/yZPCgzOHLkb1XXRUD/3KZxR/kyaYyHC0f2cS570=
dirYz92a6Ti1eVo4jbR/xEUz3iABhvx3SpnXQy4c8JyzSiNNGclQvM7WAXbSzuKP11hBfqmOsa5AfGvscWtyKwRMTUyNeJ9Wd2lrOMi49EeEzrE2
yNh0XNDVZMvvt43WzxZpVEfoOdcdctknN6DH+mdtg0jnKgflDAy42T1YC/CtEflFt3DyrI+60r7eYpkfX7dCkWBDH85U+lbvbCikd0k8dBoH87H5Qg==
2ZGzjSQ8qJPQRPpus+LXLhpWbAmEpW/XfZykfggniKemx7C1Gp58r1sBS3e6RZ6wSrGjp5/6NaoHt2RxE/J6cpvGwO8w9tx1CTi5RT9QyZ7tMAWAxBk=
A08n0IuKVA55LnnkTIrSrWow59V3WekpgOXusIt396Da3cz6/OQl9y5tcEV+CqyBVH72gD67S5QqoXTxMX1ianCydJ5Pcchfu5Y6Omr5Th9Tp9zqylIr
tW2QfvKUpQ1wb7mSEi79G+uaUNNlgKOBOofyINQb3+/HRMTYf+zfFT6AXVH1BJlMnb2Hf3xt0EhLZl0K3ltRrU1IodMDMG+lb/RDngWgyvUBbk9oCi3Z2w==
bcoUQq2gKiWEXrjOyEGNCIRXLIJ0eXsy5TONrlQjrCZxjud3cMKwcT7ltFXBOY0eo+ngUUr5gacoZD1wg1xj+c170X39orN8eDZzEBAVien8gjglaiAzmXc=
fOAEFAYLifb3sRiqxN8UMjMRXX9APRTcJEW9tdQqo+xDvPh6a9632h/3pDtKz+o76iYDgnUp6yyMtlwnNNxmaQncLhwXOylq88zG/RJCGeX065ggogwSr7ZA
uAjjXRMggTBEGpt0pee7oj3iqh1HdncUEG3rxTqU/WV8Etv11MEgpz5D3p8KCERvzlfxDWRl886xzHejK0bRth6EaYn63M9YdwXO7KqFOQIOM40DbMGR1HAOGw==
KaH6UZTvVJ1R5bdRTciIclz8M4t7mAo+IXVT9n0DrbbWqKp15evyK9Z2RnokDkWKuly8gsmqKq5TYcJbW2y0iO94lVuPT7c+wJPn7ruWc9E4euLdU/eflktebww=
MXRDmE60qUYHuXqXqFV2nThyQJaCcMkmT2MeBgEK08wZzSo14UXU7WZmwv5gV0A72bxj1NeVIIPP60Id2JnjuCwfSg5g1EbX4xGdBeQOHwbjFWqoQ7MB93r6McRA
RvExfm8ab0wqM9/p3oIwPglkkZvKxtgh68tkj49pRCRos8G1gGbt/9bBCwv1nqvTRi9y4IGw5g/RtcnGGzm7xiWWThVbFs4tVWnpMai6tas6rWLkTt9PJKyKfkvq/w==
U3iCt1dI95gxG8zDjEA1H/BasP/Wxvos31UKGN932SoDXQXhLZM5qzhoUqlbk/9wYAfmxubzAC2iDR6oReYk3v1V0dmjzhs60XJzphP9C14a8Ciju6W2LDktoaZbvEA=
trVvSeN1Ki4F905FEQnOZXamKQGY0J6hVw4Ut6WyCJzR4Jkw8fLHh5JdBR5KIXLvo+swOuYDavSDWQnKdkRQYJaxkmWPwBPUGcgREOWuWBbxY+QvKrHQYkOPZzFPXNLe
aQXkWomVggiirRDHTDxoeqmRJ34HrwqD/b02XuDiZiOHr0VrAjs3416nKMAD8BbjcxOdd8f0vspdraEUNzgp7qlqY1bDnUZj8044RMpGzkVUNqGI424OxBzfX0EGEeGOUA==
075bh4Kd3zyYJfvsEU4o0vJyqCRrbzFKiYFTeBuesCg1sNmCtDINOn8oftA/na2JtMl0PjBobFlXP/DSYmYbTIuBbLftbfIT9gcn2ITsNlOE1DdcK6d52VI1itD6JJm/WQs=
M1Mb+GE81R1ddl/woj9ee4HzVXv587QLMIpTdvjtou1PpHfgRQDQodvVLPiwJ/d5WTTLK4jChdYT6VPSBxF0mjoVGwKzUBbsgVoy4oEo/OtXKKIri575BEAK6lN5IlEaqGT8'''
la = a.split('\n')
for i in range(len(la)):
src = b64decode(la[i])
print(f"uLength: {i + 1} ====> nbits: {int(src.hex(), 16).bit_length()}")

image-20220331140122921


不过这一切在老师后面给的另一份文档中得到证实。。。

最终在js里可以这么用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* TASK */
// get real random
const gen_rand_bits = $('#gen_rand_bits');
gen_rand_bits.click(function() {
const rand_bits = $('#rand_bits');
const rand_num = $('#rand_num');

rand_num.val('');
let nbits = rand_bits.val();
if (nbits === '') {
alert('请输入合法长度');
return null;
}
if (nbits > 6148) {
alert('长度过长,须小于6148Bytes');
return null;
}
nbits = parseFloat(nbits);
p = token.SOF_GenerateRandom(nbits);
if (p === -3) {
alert("与硬件交互失败,请检查是否插入USBKEY并是否登录");
return null;
}
else if (p === -2) {
alert("生成随机数失败,错误码:", token.SOF_GetLastError());
return null;
}
else {
p = _Base64decodeArray(p);
let val = 0n;
let len = p.length;
for(let i = 0; i < len; ++i) {
val += BigInt(p[i]) * 256n ** (BigInt(len) - BigInt(i) - 1n);
}
rand_num.val(val);
}
})

此外,经过测试(多次重装mPlugin_Setup,该硬件生成的最大随机数位数在6148*8=49184 bits左右

计算HAMC

HMAC

哈希运算消息认证码,简单来说就一句话

image-20220414114142773

伪代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
function hmac (key, message) {
if (length(key) > blocksize) {
key = hash(key) // keys longer than blocksize are shortened
}
if (length(key) < blocksize) {
// keys shorter than blocksize are zero-padded (where ∥ is concatenation)
key = key ∥ [0x00 * (blocksize - length(key))] // Where * is repetition.
}
o_key_pad = [0x5c * blocksize] ⊕ key // Where blocksize is that of the underlying hash function
i_key_pad = [0x36 * blocksize] ⊕ key // Where ⊕ is exclusive or (XOR)

return hash(o_key_pad ∥ hash(i_key_pad ∥ message)) // Where ∥ is concatenation
}

注意下面框框框起来

image-20220414164004737

下面这张wiki的图就将用SHA-1做HMAC的流程解释得很清楚

image-20220905165959759

blocksize参照下表

image-20220414143856714

js实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// HAMC
const gen_hmac = $('#gen_hmac');
gen_hmac.click(function() {
const hmac_val = $('#hmac_val');
const hmac_key = $('#hmac_key');
const hmac_msg = $('#hmac_msg');
const hmac_digest = $('#hmac_digest');
const container = $('#sele_contentList');

let userID = '1234567812345678';
let key = hmac_key.val();
let msg = hmac_msg.val();
let digest = hmac_digest.val();

let key_length = key.length;
let msg_length = msg.length;

let containerName = container.find("option:selected").text();

token.SOF_SetUserID(userID);
token.SOF_SetDigestMethod(digest);

let block_size = 64;
if (digest == 130 || digest == 131) {
block_size = 128;
}

let tmp_msg = stringToByte(msg);
let tmp_key = stringToByte(key);
if (key_length > block_size) {
tmp_key = token.SOF_DigestData(containerName, _Base64encodeArray(tmp_key), key_length);
tmp_key = _Base64decodeArray(tmp_key);
}

key_length = tmp_key.length;
if (key_length < block_size) {
for (let i = 0; i < block_size - key_length; ++i) {
tmp_key.push(0x0);
}
}

var o_key_pad = new Array(block_size).fill(0x5c);
var i_key_pad = new Array(block_size).fill(0x36);

for (let i = 0; i < block_size; ++i) {
o_key_pad[i] = tmp_key[i] ^ o_key_pad[i];
i_key_pad[i] = tmp_key[i] ^ i_key_pad[i];
}

let tmp1 = i_key_pad.concat(tmp_msg);
tmp1 = token.SOF_DigestData(containerName, _Base64encodeArray(tmp1), tmp1.length);

tmp1 = _Base64decodeArray(tmp1);

let tmp2 = o_key_pad.concat(tmp1);
tmp2 = token.SOF_DigestData(containerName, _Base64encodeArray(tmp2), tmp2.length);

if (tmp2 != null && tmp2 != "") {
hmac_val.val('');
hmac_val.val(tmp2);
}
else {
alert("HMAC失败,错误码:" + token.SOF_GetLastError());
}
})

验证一下好吧

image-20220414192200667

image-20220414192219666

image-20220414191937686

image-20220414191906310

生成公私钥对

函数原型如下所示

这个会根据参数,创建一个名为containerName的容器,返回0成功。然后可以用SOF_ExportPubKeyEx将公钥导出,这个在demo里已经实现了,就不再赘述

image-20220331182215226

image-20220418162916346

导出公钥,制作数字证书,并将证书导入USBKey

文档里看到生成公私钥如上,还有生成公私钥并生成证书请求、还有将提前生成好的证书导入usbkey的API

要申请证书,还必须找一个CA

image-20220331182348971

image-20220415130636618

创建容器(有点鸡贼,调用SOF_GenerateP10Request接口,借用申请证书请求时创建的容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// create container meanwhile determine nbits of RSA or ECC
const create_con = $('#create_con');
create_con.click(function() {
let container_name = $('#container_name').val();
let key_type = $('#key_type').val();
let key_nbits = parseInt($('#key_nbits').val());
let enc_sign = parseInt($('#enc_sign').val());

let rtn = token.SOF_GenerateP10Request(container_name, '4xwi11', key_type, enc_sign, key_nbits);
if (rtn != null && rtn != '') {
alert('容器创建成功');
}
else {
alert('容器创建失败,错误代码:', token.SOF_GetLastError());
}
})

将openssl或者gmssl创建的证书导入刚才创建好的容器

其中证书的内容即自签名的证书内容,格式为base64编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// import Cert
const import_cert = $('#import_cert');
import_cert.click(function() {
let container_name = $('#container_name').val();
let import_cer_info = $('#import_cer_info').val();
let enc_sign = parseInt($('#enc_sign').val());

let res = token.SOF_ImportCert(container_name, import_cer_info, enc_sign);
if (res === 0) {
alert('导入成功');
}
else {
alert("导入失败:", token.SOF_GetLastError());
}
})

2022-04-18补充:

上面这个鸡贼的方法,当然会惨遭不测(现象是在数字签名的时候,除了老师导给我的SM2证书,我自己生成的其他证书验签都失败了)由于错得印象深刻,这里单独补充一下

原因是

image-20220418155711428

这个来自《GMT0016-2012智能IC卡及智能密码钥匙密码应用接口规范》

所以上面这个操作是完全错误的,内部的私钥和导入的证书其实是对不上的

解决的方法是,将证书请求或公钥导出来,然后给外面的CA去做证书,然后再把证书导入(注意这里不能再自签名了,因为容器内部的私钥导不出来。然后暂时没找到合适的指定公钥的证书生成工具以及这个pkcs10的证书申请我也没怎么搞懂,先咕咕咕了

同样,也是因为上面这段话,是不能内部产生加密容器的

调用USBKey进行RSA或SM2数字签名

这个demo里,直接copy,注意RSA的摘要算法不能是SM3,而SM2的摘要算法只能有SM3

image-20220418135954626

还有一点,注意userID并非鸡肋。这在最后补充SM2签名还会提到

image-20220418140233530

一些坑

虽然坑很多,但主要的还是记录下

  • 问题:key管理软件可以使用,但web端连不到

    解决:重新安装mPlugin_Setup

image-20220414113556640

补充

可以发现SM2签名,每次点签名按钮生成的签名都是不一样的,而RSA固定不变。因为前者在签名过程中使用了随机数

填下ECC和SM2签名的坑

ECDSA

image-20220418190109643

SM2DSA

在ECDSA基础上,就非常好理解了

首先,要准备用户ID,这也是为什么上文这里有SOF_SetUserID的API

image-20220418185506664

  • 签名

    image-20220418185625565

  • 验签

    image-20220418185802766

    image-20220418185818600

具体的还是看文档吧

Reference

  1. GMT0016-2012智能IC卡及智能密码钥匙密码应用接口规范
  2. 龙脉国密KEY 证书综合应用插件用户手册
  3. openssl官方文档
  4. gmssl模拟CA发布证书
  5. 外校实验课usbkey使用(他们的key管理工具都是v2.2.19的
  6. js-Bignt处理大数
  7. wiki-HAMC
  8. HMAC-blocksize
  9. https://lazzzaro.github.io/2020/11/07/crypto-ECC/
  10. SM2椭圆曲线公钥密码算法