希望帮到有缘人
USBKey Learning
证书
一些证书类型:
所谓的用户证书,就是用户用自己的私钥对对自身信息内容进行签名,然后发给CA证书签发机构,CA验证后用CA的私钥签发的数字证书
此外还要知道的文件类型
使用openssl生成p12格式的RSA和ECC证书
-
生成n位RSA密钥 $ openssl genrsa -out 4xwi11_rsa_sk.key [n]
-
从RSA密钥中提取公钥 $ openssl rsa -in 4xwi11_rsa_sk.key -pubout -out 4xwi11_rsa_pk.key
关于RSA密钥文件的直接提取或使用不妨看看这篇,Fr.https://4xwi11.github.io/posts/d77a51b9/ -
在prime256v1下生成ECC密钥(前面的参数是椭圆曲线的参数用openssl ecparam -list_curves 命令查看 $ openssl ecparam -genkey -name prime256v1 -param_enc explicit -outform pem -out 4xwi11_ec_sk.key
-
从ECC密钥中提取公钥 $ openssl ec -in 4xwi11_ec_sk.key -pubout -out 4xwi11_ec_pk.key
ECC查看私钥内容 $ openssl ecparam -in 4xwi11_ec_sk.key -text
-
用密钥生成证书 生成证书申请文件 $ openssl req -new -out 4xwi11.csr -key 4xwi11_ec_sk.key -keyform PEM
签发crt证书(这里其实是自签名 $ openssl x509 -req -in 4xwi11.csr -out 4xwi11.crt -signkey 4xwi11_ec_sk.key -CAcreateserial -days 3650
将crt转为p12 $ openssl pkcs12 -export -clcerts -in 4xwi11.crt -inkey 4xwi11_ec_sk.key -out 4xwi11.p12
使用下面命令可以查看私钥 $ openssl pkcs12 -info -in 4xwi11.p12 -nodes -nocerts
使用gmssl生成p12格式的SM2证书
安装gmssl,Fr.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的密钥
-
CA生成密钥 $ gmssl sm2 -genkey -out private/cakey.pem
-
CA自签名证书 $ gmssl req -new -x509 -key private/cakey.pem -out cacert.pem
-
客户端生成密钥 $ gmssl sm2 -genkey -out 4xwi11.key
-
客户生成csr(好像公司等信息要和CA自签时一致 $ gmssl req -new -key 4xwi11.key -out 4xwi11.csr
-
CA签发crt证书 $ gmssl ca -in 4xwi11.csr -out 4xwi11.crt -cert demoCA/cacert.pem -keyfile demoCA/private/cakey.pem
-
验证签名 $ gmssl verify -verbose -x509_strict -CAfile demoCA/cacert.pem 4xwi11.crt
-
将crt转为p12 $ gmssl pkcs12 -export -in 4xwi11.crt -inkey 4xwi11.key -out 4xwi11.p12
将导入usbkey的容器里,接着就是很迷的地方,如下图,上面那个是老师帮我用在线网站生成证书然后导出公钥和私钥的,下面那个是我自己的, 我是没找到有什么办法可以把公私钥像上面那样导出来,反正我的无法正常使用(只能对称加密。其中应该有版本的问题,我看别人的都是V2.2.19,直接导入就是上面那样,而且还有创建容器的按钮,WTF
不过这个问题是可以通过调用API来解决的,后面还会提到
运行demo
总之折腾到这里本型号demo提供的功能都能够使用了
调用智能密码钥匙实现以下功能
在所有的操作之前,先实例化mTokenPlugin 对象
var token = new mTokenPlugin();
获取随机数
函数原型如下
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*8 bit长度的随机数,即uLength 表示随机数的字节长度,并且可以传浮点数
from base64 import b64decode
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()
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=
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()}")
不过这一切在老师后面给的另一份文档中得到证实。。。
最终在js里可以这么用
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
哈希运算消息认证码,简单来说就一句话
伪代码如下
function hmac (key, message) {
if (length(key) > blocksize) {
key = hash(key)
}
if (length(key) < blocksize) {
key = key ∥ [0x00 * (blocksize - length(key))]
}
o_key_pad = [0x5c * blocksize] ⊕ key
i_key_pad = [0x36 * blocksize] ⊕ key
return hash(o_key_pad ∥ hash(i_key_pad ∥ message))
}
注意下面框框框起来
下面这张wiki的图就将用SHA-1做HMAC的流程解释得很清楚
blocksize参照下表
js实现
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());
}
})
验证一下好吧
生成公私钥对
函数原型如下所示
这个会根据参数,创建一个名为containerName的容器,返回0成功。然后可以用SOF_ExportPubKeyEx 将公钥导出,这个在demo里已经实现了,就不再赘述
导出公钥,制作数字证书,并将证书导入USBKey
文档里看到生成公私钥如上,还有生成公私钥并生成证书请求、还有将提前生成好的证书导入usbkey的API
要申请证书,还必须找一个CA
创建容器(有点鸡贼,调用SOF_GenerateP10Request 接口,借用申请证书请求时创建的容器
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编码
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证书,我自己生成的其他证书验签都失败了)由于错得我觉得比较印象深刻,这里单独补充一下
原因是
这个来自《GMT0016-2012智能IC卡及智能密码钥匙密码应用接口规范》
所以上面这个操作是完全错误的,内部的私钥和导入的证书其实是对不上的
解决的方法是,将证书请求或公钥导出来,然后给外面的CA去做证书,然后再把证书导入(注意这里不能再自签名了,因为容器内部的私钥导不出来。然后暂时没找到合适的指定公钥的证书生成工具以及这个pkcs10的证书申请我也没怎么搞懂,先咕咕咕了
同样,也是因为上面这段话,是不能内部产生加密容器的
调用USBKey进行RSA或SM2数字签名
这个demo里,直接copy,注意RSA的摘要算法不能是SM3,而SM2的摘要算法只能有SM3
还有一点,注意userID并非鸡肋。这在最后补充SM2签名还会提到
项目
编写基于web的前后端程序,用USBKey实现银行转账时的签名流程,如:输入转账信息,点击“转账”按钮,弹出对话框,让用户输入USBKey的PIN码,如果PIN码正确,则对转账信息进行数字签名并存储相关信息,否则失败
所有功能在之前都实现过了,存储感觉没什么意思,就没连数据库,简单实现了一下
一些坑
虽然坑很多,但主要的还是记录下
-
问题:key管理软件可以使用,但web端连不到 解决:重新安装mPlugin_Setup
补充
可以发现SM2签名,每次点签名按钮生成的签名都是不一样的,而RSA固定不变。因为前者在签名过程中使用了随机数
填下ECC和SM2签名的坑
ECDSA
SM2DSA
在ECDSA基础上,就非常好理解了
首先,要准备用户ID,这也是为什么上文这里有SOF_SetUserID 的API
具体的还是看文档吧
Reference.
- GMT0016-2012智能IC卡及智能密码钥匙密码应用接口规范
- 龙脉国密KEY 证书综合应用插件用户手册
- openssl官方文档
- gmssl模拟CA发布证书
- 外校实验课usbkey使用(他们的key管理工具都是v2.2.19的。。
- js-Bignt处理大数
- wiki-HAMC
- HMAC-blocksize
- https://lazzzaro.github.io/2020/11/07/crypto-ECC/
- SM2椭圆曲线公钥密码算法
|