数字签名就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。一般是非对称密钥加密技术与数字摘要技术的结合应用,目前在数字签名中使用的三种非对称算法有:
1. RSA,这个巨NB算法的实现方式,使其既可以用于签名也可以用于加密(密钥交换)。除了将公钥与密钥的地位交换一下之外,其它步骤几乎是完全一样的。发送方用自己的私钥对消息的摘要值进行“加密”,接收方使用发送方的公钥进摘要值进行“解密”,并将其与根据消息独立计算出的摘要值进行比较进行验证。如果匹配,则签名就是有效的。相比加密,RSA签名比较难懂的是分组格式化,我们常说RSA是对摘要值进行加密,其实并不一定对,在加密之会使用一个DER编码的DigestInfo结构(该结构包括HASH算法标识符+摘要值),是对这个DigestInfo结构进行加密。所有导致一般使用RSA(1204位)私钥进行加密的话,最多只能加密117字节数据,而不是128字节(1024位)。
这样做的目的,是用来防止攻击。假设某个HASH算法(H1)遭受灾难性破坏,有可能产生消息摘要为定值的随机消息(BTW:HASH算法也不能证明完全没有问题,只是在给定时间你产生不了相同的冲撞就认为是安全的,L)。攻击过程:
1)使用另一种不同的算法H2对消息M进行签名
2)攻击者可以使用H1(M’)=H2(M)的特性产生一个新的消息M’。
将我们的签名附加于他新产生的消息M’上(标注用已攻破的散列算法H1进行),该签名仍然被验证。
2.DSA签名:当初的设计是用于数字签名而不用于密钥交换的算法,与DH一样的密码数学基础,即素数域上中的模幂运算,最重要的区别构造p(大素数)时要使p-1可以被另一个称作q的素数除尽。与RSA不同的是,DSA签名结果是由两个值r和s(160位)组成。
Openssl官网给出的签名api及参数说明:
#include <openssl/dsa.h>
int DSA_sign(int type, const unsigned char *dgst, int len,
unsigned char *sigret, unsigned int *siglen, DSA *dsa);
int DSA_verify(int type, const unsigned char *dgst, int len,
unsigned char *sigbuf, int siglen, DSA *dsa);
DSA_sign() computes a digital signature on the len byte message digest dgst using the private key dsa and places its ASN.1 DER encoding at sigret. The length of the signature is places in *siglen. sigret must point to DSA_size(dsa) bytes of memory.
DSA_verify() verifies that the signature sigbuf of size siglen matches a given message digest dgst of size len. dsa is the signer's public key.
The type parameter is ignored.
通过DSA_sign可知,签名结果保存在无符号字符串指针sigret中,并没有体现出r和s两部分的内容,这是因为在DSA_sign内部对签名结果进行了字符编码转换,将r和s转换为了ASN.1 DER encoding并将其保存到了sigret。
对openssl源码进行分析对上述进行验证(涉及到的文件均在目录D:\openssl-1.0.1e\crypto\dsa):
在源文件中找到dsa_asn1.c的DSA_sign的源代码:
int DSA_sign(int type, const unsigned char *dgst, int dlen, unsigned char *sig,unsigned int *siglen, DSA *dsa) { DSA_SIG *s; RAND_seed(dgst, dlen); s=DSA_do_sign(dgst,dlen,dsa); if (s == NULL) { *siglen=0; return(0); } *siglen=i2d_DSA_SIG(s,&sig); DSA_SIG_free(s); return(1); }其中结构体DSA_SIG在dsa.h中的定义如下:
typedef struct DSA_SIG_st { BIGNUM *r; BIGNUM *s; } DSA_SIG;
在 DSA_sign中通过*siglen=i2d_DSA_SIG(s,&sig);将签名结果s(结构体DSA_SIG)转码为了无符号字符串指针sig。
通过上面的分析,我们可以得到dsa签名与rsa签名还是有一定的不同。
3.ECDSA是ECC与DSA的结合,整个签名过程与DSA类似,所不一样的是签名中采取的算法为ECC,最后签名出来的值也是分为r,s。
签名过程如下:
1、选择一条椭圆曲线Ep(a,b),和基点G;
2、选择私有密钥k(k<n,n为G的阶),利用基点G计算公开密钥K=kG;
3、产生一个随机整数r(r<n),计算点R=rG;
4、将原数据和点R的坐标值x,y作为参数,计算SHA1做为hash,即Hash=SHA1(原数据,x,y);
5、计算s≡r - Hash * k (mod n)
6、r和s做为签名值,如果r和s其中一个为0,重新从第3步开始执行
验证过程如下:
1、接受方在收到消息(m)和签名值(r,s)后,进行以下运算
2、计算:sG+H(m)P=(x1,y1), r1≡ x1 mod p。
3、验证等式:r1 ≡ r mod p。
4、如果等式成立,接受签名,否则签名无效。
以下是openssl源码的部分api(D:\openssl-1.0.1e\crypto\ecdsa)
int ECDSA_sign(int type, const unsigned char *dgst, int dlen, unsigned char *sig, unsigned int *siglen, EC_KEY *eckey) { return ECDSA_sign_ex(type, dgst, dlen, sig, siglen, NULL, NULL, eckey); } int ECDSA_sign_ex(int type, const unsigned char *dgst, int dlen, unsigned char *sig, unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey) { ECDSA_SIG *s; RAND_seed(dgst, dlen); s = ECDSA_do_sign_ex(dgst, dlen, kinv, r, eckey); if (s == NULL) { *siglen=0; return 0; } *siglen = i2d_ECDSA_SIG(s, &sig); ECDSA_SIG_free(s); return 1; } typedef struct ECDSA_SIG_st { BIGNUM *r; BIGNUM *s; } ECDSA_SIG;
在\crypto\ec\ec_lcl.h中有EC_KEY的定义:
struct ec_key_st { int version; EC_GROUP *group; EC_POINT *pub_key; BIGNUM *priv_key; unsigned int enc_flag; point_conversion_form_t conv_form; int references; int flags; EC_EXTRA_DATA *method_data; } /* EC_KEY */; struct ec_point_st { const EC_METHOD *meth; /* All members except 'meth' are handled by the method functions, * even if they appear generic */ BIGNUM X; BIGNUM Y; BIGNUM Z; /* Jacobian projective coordinates: * (X, Y, Z) represents (X/Z^2, Y/Z^3) if Z != 0 */ int Z_is_one; /* enable optimized point arithmetics for special case */ } /* EC_POINT */;
关于PKI的一些内容,可以参考新浪微博,博主为凡人樊语的一些文章:http://blog.sina.com.cn/u/1437143324