實現數字簽名
數字簽名算法(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.PrivateKeypriv.Parameters = params
if e := dsa.GenerateKey(&priv, rand.Reader); e !=
nil {fmt.Println(e)}pub := priv.PublicKeymessage := []
byte(
"hello world")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數據安全公司及其合作伙伴制定的一組公鑰密碼學標準,其中包括證書申請、證書更新、證書作廢表發布、擴展證書內容以及數字簽名、數字信封的格式等方面的一系列相關協議。
簽名認證:
package mainimport (
"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)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
}
func Sign(data []
byte, privkey *ecdsa.PrivateKey) ([]
byte, error) {digest := sha256.Sum256(data)r, s, err := ecdsa.Sign(rand.Reader, privkey, digest[:])
if err !=
nil {
return nil, err}params := privkey.Curve.Params()curveOrderByteSize := params.P.BitLen() /
8rBytes, 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
}
func Verify(data, signature []
byte, pubkey *ecdsa.PublicKey)
bool {digest := sha256.Sum256(data)curveOrderByteSize := pubkey.Curve.Params().P.BitLen() /
8r, s :=
new(big.Int),
new(big.Int)r.SetBytes(signature[:curveOrderByteSize])s.SetBytes(signature[curveOrderByteSize:])
return ecdsa.Verify(pubkey, digest[:], r, s)
}
總結
以上是生活随笔為你收集整理的实现数字签名的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。