实现数字签名
实现数字签名
数字签名算法(DSA)
数字签名算法(DSA,Digital Signature Algorithm),是一种公开**算法,不能用于加密,只能用于数字签名。主要用作为接收者验证数字的完整性和数据发送者的身份,DSA算法的安全性基于解离散对数的困难性。
package main
import (
"crypto/dsa"
"crypto/rand"
"fmt"
)
func main() {
var params dsa.Parameters
//生成参数
if e := dsa.GenerateParameters(¶ms, rand.Reader, dsa.L1024N160); e != nil {
fmt.Println(e)
}
//生成私钥
var priv dsa.PrivateKey
priv.Parameters = params
if e := dsa.GenerateKey(&priv, rand.Reader); e != nil {
fmt.Println(e)
}
//根据私钥生成公钥
pub := priv.PublicKey
//消息
message := []byte("hello world")
//使用私钥进行签名,产生整数对(r,s)
r, s, e := dsa.Sign(rand.Reader, &priv, message)
if e != nil {
fmt.Println(e)
}
//认证
fmt.Printf("认证 %q (r:%s,s:%s)\n", message, r, s)
if dsa.Verify(&pub, message, r, s) {
fmt.Println("认证正确!")
} else {
fmt.Println("认证失败!")
}
}
公钥加密算法(RSA)
公钥加密算法于1987年首次公开,RSA是提出这个算法的三人姓氏开头字母组成,可用于加密,也可以用于数字签名。RSA的安全性基于大数分解的困难性。
加密算法:
最优非对称加密填充(OAEP,Optimal Asymmetric Encryption Padding),在随机预言模型下,用来处理非对称加密前的明文;
公钥密码学标准(PKCS,The Public-Key Cryptography Standards),是由美国RSA数据安全公司及其合作伙伴制定的一组公钥密码学标准,其中包括证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议。
签名认证:
公钥密码学标准(PKCS);
概率签名方案(PSS,Probabilistic Signature Scheme),与PKCS不同的是,它支持添加盐(Salt)。
package main
import (
"crypto"
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"fmt"
)
func main() {
//生成私钥
priv, e := rsa.GenerateKey(rand.Reader, 1024)
if e != nil {
fmt.Println(e)
}
//根据私钥产生公钥
pub := &priv.PublicKey
//明文
plaintext := []byte("Hello world")
//加密生成密文
fmt.Printf("%q\n加密:\n", plaintext)
ciphertext, e := rsa.EncryptOAEP(md5.New(), rand.Reader, pub, plaintext, nil)
if e != nil {
fmt.Println(e)
}
fmt.Printf("\t%x\n", ciphertext)
//解密得到明文
fmt.Printf("解密:\n")
plaintext, e = rsa.DecryptOAEP(md5.New(), rand.Reader, priv, ciphertext, nil)
if e != nil {
fmt.Println(e)
}
fmt.Printf("\t%q\n", plaintext)
//消息先进行Hash处理
h := md5.New()
h.Write(plaintext)
hashed := h.Sum(nil)
fmt.Printf("%q MD5 Hashed:\n\t%x\n", plaintext, hashed)
//签名
opts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthAuto, Hash: crypto.MD5}
sig, e := rsa.SignPSS(rand.Reader, priv, crypto.MD5, hashed, opts)
if e != nil {
fmt.Println(e)
}
fmt.Printf("签名:\n\t%x\n", sig)
//认证
fmt.Printf("验证结果:")
if e := rsa.VerifyPSS(pub, crypto.MD5, hashed, sig, opts); e != nil {
fmt.Println("失败:", e)
} else {
fmt.Println("成功.")
}
}
椭圆曲线加密算法
ECDSA的全名是Elliptic Curve DSA,即椭圆曲线DSA。它是Digital Signature Algorithm (DSA)应用了椭圆曲线加密算法的变种。椭圆曲线算法的原理很复杂,但是具有很好的公开**算法特性,通过公钥无法逆向获得私钥。
-
签名过程
假设要签名的消息是一个字符串:“Hello World!”。DSA签名的第一个步骤是对待签名的消息生成一个消息摘要。不同的签名算法使用不同的消息摘要算法。而ECDSA256使用SHA256生成256比特的摘要。
摘要生成结束后,应用签名算法对摘要进行签名:
产生一个随机数k
利用随机数k,计算出两个大数r和s。将r和s拼在一起就构成了对消息摘要的签名。
这里需要注意的是,因为随机数k的存在,对于同一条消息,使用同一个算法,产生的签名是不一样的。从函数的角度来理解,签名函数对同样的输入会产生不同的输出。因为函数内部会将随机值混入签名的过程。 验证过程
关于验证过程,这里不讨论它的算法细节。从宏观上看,消息的接收方从签名中分离出r和s,然后利用公开的**信息和s计算出r。如果计算出的r和接收到的r值相同,则表示验证成功。否则,表示验证失败。
package main
import (
"fmt"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"math/big"
)
func main() {
//明文
message := []byte("Hello world")
key, err := NewSigningKey()
if err != nil {
return
}
signature, err := Sign(message, key)
fmt.Printf("签名后:%x\n", signature)
if err != nil {
return
}
if !Verify(message, signature, &key.PublicKey) {
fmt.Println("验证失败!")
return
}else{
fmt.Println("验证成功!")
}
}
func NewSigningKey() (*ecdsa.PrivateKey, error) {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
return key, err
}
// Sign signs arbitrary data using ECDSA.
func Sign(data []byte, privkey *ecdsa.PrivateKey) ([]byte, error) {
// hash message
digest := sha256.Sum256(data)
// sign the hash
r, s, err := ecdsa.Sign(rand.Reader, privkey, digest[:])
if err != nil {
return nil, err
}
// encode the signature {R, S}
// big.Int.Bytes() will need padding in the case of leading zero bytes
params := privkey.Curve.Params()
curveOrderByteSize := params.P.BitLen() / 8
rBytes, sBytes := r.Bytes(), s.Bytes()
signature := make([]byte, curveOrderByteSize*2)
copy(signature[curveOrderByteSize-len(rBytes):], rBytes)
copy(signature[curveOrderByteSize*2-len(sBytes):], sBytes)
return signature, nil
}
// Verify checks a raw ECDSA signature.
// Returns true if it's valid and false if not.
func Verify(data, signature []byte, pubkey *ecdsa.PublicKey) bool {
// hash message
digest := sha256.Sum256(data)
curveOrderByteSize := pubkey.Curve.Params().P.BitLen() / 8
r, s := new(big.Int), new(big.Int)
r.SetBytes(signature[:curveOrderByteSize])
s.SetBytes(signature[curveOrderByteSize:])
return ecdsa.Verify(pubkey, digest[:], r, s)
}