【密码学五】数字签名、RSA实现数字签名和验证
消息認證碼&數(shù)字簽名
消息認證碼(message authentication code)是一種確認完整性并進行認證的技術(shù),取三個單詞的首字母,簡稱為MAC。
消息認證碼的輸入包括任意長度的消息和一個發(fā)送者與接收者之間共享的密鑰,它可以輸出固定長度的數(shù)據(jù),這個數(shù)據(jù)稱為MAC值。
根據(jù)任意長度的消息輸出固定長度的數(shù)據(jù),這一點和單向散列函數(shù)很類似。但是單向散列函數(shù)中計算散列值時不需要密鑰,而消息認證碼中則需要使用發(fā)送者與接收者之間共享的密鑰。
要計算MAC必須持有共享密鑰,沒有共享密鑰的人就無法計算MAC值,消息認證碼正是利用這一性質(zhì)來完成認證的。此外,和單向散列函數(shù)的散列值一樣,哪怕消息中發(fā)生1比特的變化,MAC值也會產(chǎn)生變化,消息認證碼正是利用這一性質(zhì)來確認完整性的。
消息認證碼有很多種實現(xiàn)方法,大家可以暫且這樣理解:消息認證碼是一種與密鑰相關(guān)聯(lián)的單向散列函數(shù)。
Alice 和 Bob 的故事
像以前一樣,我們還是從一個Alice和Bob的故事開始講起。不過,這一次Alice和Bob分別是兩家銀行,Alice銀行通過網(wǎng)絡(luò)向Bob銀行發(fā)送了一條匯款請求,Bob銀行收到的請求內(nèi)容是:
從賬戶A?5374向賬戶B?6671匯款1000萬元從賬戶A-5374 向賬戶B-6671匯款1000萬元 從賬戶A?5374向賬戶B?6671匯款1000萬元
當(dāng)然,Bob銀行所收到的匯款請求內(nèi)容必須與Alice銀行所發(fā)送的內(nèi)容是完全一致的。如果主動攻擊者Mallory在中途將Alice銀行發(fā)送的匯款請求進行了篡改,那么Bob銀行就必須要能夠識別出這種篡改,否則如果Mallory將收款賬戶改成了自己的賬戶,那么1000萬元就會被盜走。
話說回來,這條匯款請求到底是不是Alice銀行發(fā)送的呢?有可能Alice銀行根本就沒有發(fā)送過匯款請求,而是由主動攻擊者Mallory偽裝成Alice銀行發(fā)送的。如果匯款請求不是來自Alice銀行,那么就絕對不能執(zhí)行匯款。
現(xiàn)在我們需要關(guān)注的問題是匯款請求(消息)的 “完整性" 和 “認證" 這兩個性質(zhì)。
消息的完整性(integrity), 指的是:消息沒有被篡改"這一性質(zhì),完整性也叫一致性。如果能夠確認匯款請求的內(nèi)容與Alice銀行所發(fā)出的內(nèi)容完全一致,就相當(dāng)于是確認了消息的完整性,也就意味著消息沒有被篡改。
消息的認證(authentication)指的是“消息來自正確的發(fā)送者"這一性質(zhì)。如果能夠確認匯款請求確實來自Alice銀行,就相當(dāng)于對消息進行了認證,也就意味著消息不是其他人偽裝成發(fā)送者所發(fā)出的。
通過使用本章中要介紹的消息認證碼,我們就可以同時識別出篡改和偽裝,也就是既可以確認消息的完整性,也可以進行認證。
數(shù)字簽名是一種將相當(dāng)于現(xiàn)實世界中的蓋章、簽字的功能在計算機世界中進行實現(xiàn)的技術(shù)。使用數(shù)字簽名可以識別篡改和偽裝,還可以防止否認。
從消息認證到數(shù)字簽名
-
消息認證碼的局限性
消息認證碼,我們可以識別消息是否被篡改或者發(fā)送者身份是否被偽裝,也就是可以校驗消息的完整性,還可以對消息進行認證。然而,比如在出具借條的場景中卻無法使用消息認證碼,因為消息認證碼無法防止否認。
消息認證碼之所以無法防止否認,是因為消息認證碼需要在發(fā)送者Alice和接收者Bob兩者之間共享同一個密鑰。正是因為密鑰是共享的,所以能夠使用消息認證碼計算出正確MAC值的并不只有發(fā)送者Alice,接收者Bob也可以計算出正確的MAC值。由于Alice和Bob雙方都能夠計算出正確的MAC值,因此對于第三方來說,我們無法證明這條消息的確是由Alice生成的。
-
通過數(shù)字簽名解決問題
下面請大家開動一下腦筋。假設(shè)發(fā)送者Alice和接收者Bob不需要共享一個密鑰,也就是說,Alice和Bob各自使用不同的密鑰。
我們假設(shè)Alice使用的密鑰是一個只有Alice自己才知道的私鑰。當(dāng)Alice發(fā)送消息時,她用私鑰生成一個“簽名"。相對地,接收者Bob則使用一個和Alice不同的密鑰對簽名進行驗證。使用Bob的密鑰無法根據(jù)消息生成簽名,但是用Bob的密鑰卻可以對Alice所計算的簽名進行驗證,也就是說可以知道這個簽名是否是通過Alice的密鑰計算出來的。如果真有這么一種方法的話,那么不管是識別篡改、偽裝還是防止否認就都可以實現(xiàn)了吧 ?
實際上,這種看似很神奇的技術(shù)早就已經(jīng)問世了,這就是數(shù)字簽名(digital signature)。
簽名的生成和驗證
讓我們來稍微整理一下。
在數(shù)字簽名技術(shù)中,出現(xiàn)了下面兩種行為:
- 生成消息簽名的行為
- 驗證消息簽名的行為
生成消息簽名這一行為是由消息的發(fā)送者Alice來完成的,也稱為“對消息簽名”。生成簽名就是根據(jù)消息內(nèi)容計算數(shù)字簽名的值,這個行為意味著 “我認可該消息的內(nèi)容"。
驗證數(shù)字簽名這一行為一般是由消息的接收者Bob來完成的,但也可以由需要驗證消息的第三方來完成,這里的第三方我們暫且將其命名為驗證者Victor。驗證簽名就是檢查該消息的簽名是否真的屬于Alice,驗證的結(jié)果可以是成功或者失敗,成功就意味著這個簽名是屬于Alice的,失敗則意味著這個簽名不是屬于Alice的。
在數(shù)字簽名中,生成簽名和驗證簽名這兩個行為需要使用各自專用的密鑰來完成。
Alice使用“簽名密鑰"來生成消息的簽名,而Bob和Victor則使用“驗證密鑰"來驗證消息的簽名。數(shù)字簽名對簽名密鑰和驗證密鑰進行了區(qū)分,使用驗證密鑰是無法生成簽名的。這一點非常重要。此外,簽名密鑰只能由簽名的人持有,而驗證密鑰則是任何需要驗證簽名的人都可以持有。
通過RSA實現(xiàn)數(shù)字簽名
前邊章節(jié)已經(jīng)介紹過了如何通過自己編寫的go代碼生成非對稱加密算法RSA的公鑰和私鑰文件, 假設(shè)公鑰文件的文件名為 public.pem,私鑰文件對應(yīng)的文件名為 private.pem。
生成數(shù)字簽名
// SignatureRSA // myData 要簽名的數(shù)據(jù) func SignatureRSA(myData []byte) ([]byte, error) {// 1. 從秘鑰文件中讀生成的秘鑰內(nèi)容fp, err := os.Open("private.pem")if err != nil {return nil, errors.New("打開私鑰文件 - private.pem 失敗")}// 2. 讀文件內(nèi)容fileInfo, _ := fp.Stat()all := make([]byte, fileInfo.Size())_, err = fp.Read(all)if err != nil {return nil, errors.New("讀文件內(nèi)容失敗")}fmt.Println("文件內(nèi)容: ", string(all))// 3. 關(guān)閉文件defer fp.Close()// 4. 將數(shù)據(jù)解析成pem格式的數(shù)據(jù)塊block, _ := pem.Decode(all)// 5. 解析pem數(shù)據(jù)塊, 得到私鑰privKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)if err != nil {return nil, errors.New("解析私鑰失敗")}// 6. 將數(shù)據(jù)通過哈希函數(shù)生成信息摘要myHash := sha256.New()myHash.Write(myData)result := myHash.Sum(nil)// 7. 生成簽名mySignature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA256, result)if err != nil {return nil, errors.New("生成簽名失敗")}return mySignature, nil }驗證數(shù)字簽名
// VerifyRSA // src 簽名數(shù)據(jù) // myData 元數(shù)據(jù) func VerifyRSA(src, myData []byte) error {// 1. 從秘鑰文件中讀生成的秘鑰內(nèi)容fp, err := os.Open("public.pem")if err != nil {return errors.New("打開公鑰文件 - public.pem 失敗")}// 2. 讀文件內(nèi)容fileInfo, _ := fp.Stat()all := make([]byte, fileInfo.Size())num, err := fp.Read(all)if err != nil {return errors.New("讀文件內(nèi)容失敗")}fmt.Println("文件大小: ", num)// 3. 關(guān)閉文件defer fp.Close()// 4. 將公鑰數(shù)據(jù)解析為pem格式的數(shù)據(jù)塊block, _ := pem.Decode(all)// 5. 將公鑰從pem數(shù)據(jù)塊中提取出來pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)if err != nil {return errors.New("解析公鑰失敗")}// 6. 公鑰接口轉(zhuǎn)換為公鑰對象pubKey := pubInterface.(*rsa.PublicKey)// 7. 將數(shù)據(jù)通過哈希函數(shù)生成信息摘要myHash := sha256.New()myHash.Write(myData)result := myHash.Sum(nil)// 7. 數(shù)據(jù)認證err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, result, src)if err != nil {return err}fmt.Println("數(shù)字簽名驗證成功, 恭喜o(* ̄︶ ̄*)o恭喜")return nil }總結(jié)
以上是生活随笔為你收集整理的【密码学五】数字签名、RSA实现数字签名和验证的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux命令查看服务器的型号、序列号、
- 下一篇: <Zhuuu_ZZ>那些年我们踩过的Ha