本文章同时发布于:
MediumiT 邦帮忙大家好,继上次Week39 - 各种安全性演算法的应用 - 窃听、电子欺骗实作之后,这次要介绍窜改
、抵赖
的实作。
实作
以下实作大量参考Golang RSA encrypt and decrypt example与Golang: aes-256-cbc examples (with iv, blockSize)。
并且全部的範例都在此,请先 clone 下来会较好理解。
窜改(falsification)
还记得Week38 - 「窜改(falsification)是什么?」章节防範的方法吗?就是
与电子欺骗的解决方法相同,传输人员在资料上产生一笔独一无二的代码供另一端验证
接下来就要利用产生独一无二的代码
来实作,方法有 2 种:
数位签章(Digital Signature):
拥有私钥与公钥 2 把钥匙公钥可以给任何人,私钥不可以给自己以外的人使用私钥产生代码,公钥验证代码讯息识别码(Message authentication code):
拥有 1 把钥匙这把钥匙不可以给不够信任的人使用此钥匙来产生代码小明
与早餐店阿姨
如果不够信任彼此
,会採用数位签章
,因为採用讯息识别码
的话,小明
把钥匙给早餐店阿姨
,早餐店阿姨
拿去冒名成小明
就不好了,
使用数位签章
加在原本的循序图串起来就会如下:
看看程式码,进入week40/spoofing/signature
的资料夹:
$ cd week40/spoofing/signature
里头有以下档案:
.├── badGuyKey : 坏人的私钥├── badGuyKey.pub : 坏人的公钥├── goodGuykey : 小明的私钥├── goodGuykey.pub: 小明的公钥└── main.go : 程式码
key
与key.pub
是透过ssh-keygen
这个软体来产生的,使用以下指令可以产生一组RSA
公私钥:
$ ssh-keygen -t rsa -f <key's name> -m peme.g.$ ssh-keygen -t rsa -f goodGuykey -m pem
输入后会询问是否要设定passphrase
,这是一个安全密码,如果设定了,以后使用此私钥还要输入此安全密码才可使用,以增加安全性,此範例没有设定。
code 的方面主要可以看main
的部分,注解有解释流程,搭配循序图会较好理解:
// 大量参考: https://gist.github.com/mfridman/c0c5ece512f63d429c4589196a1d4242package mainimport ("crypto""crypto/rand""crypto/rsa""crypto/sha512""crypto/x509""encoding/pem""fmt""io/ioutil""log")// LoadFile load the file to bytesfunc LoadFile(path string) []byte {content, err := ioutil.ReadFile(path)if err != nil {log.Fatal(err)}return content}// BytesToPrivateKey bytes to private keyfunc BytesToPrivateKey(priv []byte) *rsa.PrivateKey {block, _ := pem.Decode(priv)enc := x509.IsEncryptedPEMBlock(block)b := block.Bytesvar err errorif enc {log.Println("is encrypted pem block")b, err = x509.DecryptPEMBlock(block, nil)if err != nil {log.Fatal(err)}}key, err := x509.ParsePKCS1PrivateKey(b)if err != nil {log.Fatal(err)}return key}func main() {// 坏人的私钥badGuyPrivateKey := BytesToPrivateKey(LoadFile("./badGuyKey"))// 小明的私钥goodGuyPrivateKey := BytesToPrivateKey(LoadFile("./goodGuyKey"))// 小明的公钥,公钥可以透过私要来取得,所以这边就不在载入公钥档案了goodGuyPublicKey := goodGuyPrivateKey.PublicKey// 小明用自己的私钥对讯息签章messageBytes := []byte("小明餐点: 大冰奶")hash := sha512.New()hash.Write(messageBytes)hashed := hash.Sum(nil)// 小明用自己的私钥签名signature, err := rsa.SignPKCS1v15(rand.Reader, goodGuyPrivateKey, crypto.SHA512, hashed)if err != nil {panic(err)}// 小明的资料被坏人拦截,坏人开始伪造小明的讯息messageBytes = []byte("小明餐点: 大冰红")hash = sha512.New()hash.Write(messageBytes)hashed = hash.Sum(nil)// 坏人用自己的私钥签名,并非小明的signature, err = rsa.SignPKCS1v15(rand.Reader, badGuyPrivateKey, crypto.SHA512, hashed)if err != nil {panic(err)}// 早餐店阿姨取得小明的公钥,利用此公钥验证之后发现不是小明传的讯息err = rsa.VerifyPKCS1v15(&goodGuyPublicKey, crypto.SHA512, hashed, signature)if err != nil {fmt.Println("Two signatures are not the same. Error: ", err)return}}
小明
与早餐店阿姨
如果够信任彼此
,甚至他们可能是同一个系统,那就不必担心早餐店阿姨
拿小明
的钥匙做坏事了,故可採用讯息识别码
,
使用讯息识别码
加在原本的循序图串起来就会如下:
看看程式码,进入week40/falsification/HMAC
的资料夹:
$ cd week40/falsification/HMAC
里头有以下档案:
.└── main.go : 程式码
code 主要可以看main
的部分,注解有解释流程,搭配循序图会较好理解:
package mainimport ("crypto/hmac""crypto/sha256""encoding/hex""fmt")func hmacSha256(data string, secret string) string {h := hmac.New(sha256.New, []byte(secret))h.Write([]byte(data))return hex.EncodeToString(h.Sum(nil))}func main() {sharedSecret := "小明与早餐店阿姨的共同钥匙"badGuySecret := "坏人的钥匙"meals := "小明餐点: 大冰红"// 小明利用与早餐店阿姨共同的钥匙产生HMACrequestHMAC := hmacSha256(meals, sharedSecret)// 坏人拦截小明的封包,利用自己的钥匙产生HMACrequestHMAC := hmacSha256(meals, badGuySecret)// 早餐店阿姨利用与小明共同的钥匙产生HMACtrueHMAC := hmacSha256(meals, sharedSecret)// 早餐店阿姨比对此两个HMAC,发现不同,故此讯息不是小明传送的if requestHMAC != trueHMAC {fmt.Println("Two HMACs are not the same.")return}}
抵赖(repudiation)
还记得上篇文章 - 「抵赖(repudiation)是什么?」章节防範的方法吗?就是
要求传输人员在资料上产生一笔独一无二的代码供另一端验证
接下来就要利用产生独一无二的代码
来实作,由于在抵赖
的情境中,小明
就是坏人,所以早餐店阿姨
与小明
本来就不够信任彼此
,所以只可用 1 种方法:
数位签章(Digital Signature):
拥有私钥与公钥 2 把钥匙公钥可以给任何人,私钥不可以给自己以外的人使用私钥产生代码,公钥验证代码使用数位签章
加在原本的循序图串起来就会如下:
看看程式码,进入week40/repudiation/signature
的资料夹:
$ cd week40/repudiation/signature
里头有以下档案:
.├── goodGuyKey├── goodGuyKey.pub├── key├── key.pub└── main.go
钥匙都是透过ssh-keygen
产生,可以参考上方窜改(falsification)
章节的讲解,就不赘述。
code 主要可以看main
的部分,注解有解释流程,搭配循序图会较好理解:
// 大量参考: https://gist.github.com/mfridman/c0c5ece512f63d429c4589196a1d4242package mainimport ("crypto""crypto/rand""crypto/rsa""crypto/sha512""crypto/x509""encoding/pem""fmt""io/ioutil""log")// LoadFile load the file to bytesfunc LoadFile(path string) []byte {content, err := ioutil.ReadFile(path)if err != nil {log.Fatal(err)}return content}// BytesToPrivateKey bytes to private keyfunc BytesToPrivateKey(priv []byte) *rsa.PrivateKey {block, _ := pem.Decode(priv)enc := x509.IsEncryptedPEMBlock(block)b := block.Bytesvar err errorif enc {log.Println("is encrypted pem block")b, err = x509.DecryptPEMBlock(block, nil)if err != nil {log.Fatal(err)}}key, err := x509.ParsePKCS1PrivateKey(b)if err != nil {log.Fatal(err)}return key}func main() {goodGuyPrivateKey := BytesToPrivateKey(LoadFile("./goodGuyKey"))// 小明的公钥,公钥可以透过私要来取得,所以这边就不在载入公钥档案了goodGuyPublicKey := goodGuyPrivateKey.PublicKey// 小明用自己的私钥对讯息签章messageBytes := []byte("小明餐点: 大冰奶")hash := sha512.New()hash.Write(messageBytes)hashed := hash.Sum(nil)// 小明用自己的私钥签名signature, err := rsa.SignPKCS1v15(rand.Reader, goodGuyPrivateKey, crypto.SHA512, hashed)if err != nil {panic(err)}// 早餐店阿姨取得小明的公钥,验证后发现的确是小明,传送餐点回去给小明err = rsa.VerifyPKCS1v15(&goodGuyPublicKey, crypto.SHA512, hashed, signature)if err != nil {fmt.Println("Two signatures are not the same. Error: ", err)return}fmt.Println("Verify the signature is correct")// 小明获得餐点,并且吃完后开市赖帐// 早餐店阿姨说明当初`rsa.VerifyPKCS1v15`利用小明的公钥验证后的确是小明用私钥签章的,故证明小明确实有点过餐}