您的位置:首页 > 其它

调试国密算法验签遇到的问题

2017-07-08 00:00 246 查看
项目要求服务端与客户端支持国密证书建立ssl通道.在本地demo中可以建立完整的ssl连接并通信.

服务端建立ssl时直接使用openssl加载根证书,服务端证书,服务端私钥.这里毫无问题.

但是客户端必须调用windows的csp接口,而且windows系统目前不支持导入国密证书,所以建立连接的方式只能是通过usekey的形式(usebkey可以想象为银行u盾),usbkey中实现了csp接口.

问题1,handshake摘要

openssl在建立连接的过程中,会将握手过程的信息做摘要保存在handshake_dgst[]数组,数组中每个索引对应一种摘要算法,每种支持的摘要算法都算一遍摘要保存.

摘要算法的过程有三个,1初始化init ,2 update ,3 final.其有一个特点,就是中间的update可以分多次调用,即

init();update("123");final(); 等同于init();update("1");update("2");update("3");final();

握手过程中,未避免保存大量待摘要内容,每次握手都是增量调用摘要update,在最后一次握手时调用final就可以了.

国密算法要求计算摘要前需要计算Z值,计算Z值需要对方的公钥,而且usbkey只支持在内部计算摘要并签名,不能只调用其签名接口,这就产生了如下矛盾:

1.客户端必须保存签名前的所有握手信息,极其占用空间

2.服务端必须在第一次握手时就计算出Z值加在握手信息前摘要,否则无法验签.而第一次握手时服务端没有客户端公钥无法计算Z值,所以服务端也必须保存一份极占空间的握手信息,这种做法在访问量大的时候是个灾难.

解决方法:只要能够保证握手信息的完整性,并且服务端和客户端信息一致就可以.所以不对原始握手信息进行摘要,而对openssl计算的摘要信息进行再摘要.逻辑如下:

客户端:sign( 摘要(Z值+openssl摘要) ) 服务端:verify( 摘要(Z值+openssl摘要) )

问题2:大端小端问题

在签名与验签过程中,始终不能通过,打印过程中的参数都没有错误.最后确认为:usbkey实现的csp接口返回的签名是已小端方式存储的.服务端实现的是大端存储方式的验签算法.所以将csp接口返回的签名(r,s)分别倒序,验签通过.大小端区别如下: 转载自http://blog.csdn.net/zhaoshuzhaoshu/article/details/37600857/

所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;

所谓的小端模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。

2.为什么会有大小端:

为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

3.大小端在内存中的存放方式举例:

例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址
0x4000
0x4001
存放内容
0x34
0x12
而在Big-endian模式CPU内存中的存放方式则为:

内存地址
0x4000
0x4001
存放内容
0x12
0x34
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  国密 usbkey 签名 验签