最近在写数字签名,理想中的签名过程如下:
然后自己通过go和c++的openssl库,显示sha256进行哈希计算,然后用rsa私钥加密,最后得到一样的签名,然后需要提供一个本地网页端的签名html给调用者使用,用的是jsrsasign.js
var sig = new KJUR.crypto.Signature({"alg": "SHA256withRSA"});
sig.init(decodedPriKey);
sig.updateString(strUrl);
var hSigVal = sig.sign();
var sign = hextob64(hSigVal);
document.getElementById("signedStr").value = sign
但是此处得到的签名跟go和c++的不一样,本人找到一个在线签名网址:
http://www.metools.info/code/c82.html,本人本地的js签名跟这个在线签名网站产生的签名是一样的。
但是这个js提供的方法是将hash和rsa加密放到一块了。
最终通过调试go代码,修改一些参数,得到了跟js一样的签名值。
知晓了js在hash值计算之后,又会对此hash进行前缀的填充,填充的前缀跟hash算法有关,本人从go的签名算法函数中,得到了答案,如下所示:
其中,函数参数的第三个参数代表的是哈希算法,第四个参数hashed是被哈希过的字符串,各个hash算法都有自己的前缀,如下所示。代码中的copy(em[k-tLen:k-hashLen], prefix)则是前缀填充。
var hashPrefixes = map[crypto.Hash][]byte{
crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
crypto.MD5SHA1: {}, // A special TLS case which doesn't use an ASN1 prefix.
crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14},
}
hash的枚举值如下,如果用户在加密时,指定hash为0,则表示不进行前缀填充。
本人用的就是hash为0的填充,如下所示,红色部分若改成crypto.SHA256,则最终的签名结果跟网页端一致。
所以网页端的签名过程如下: