Week40 - 各种安全性演算法的应用 - 窜改、抵赖实作 [高智能方程序系列]

本文章同时发布於:


大家好,继上次Week39 - 各种安全性演算法的应用 - 窃听、电子欺骗实作之後,这次要介绍窜改抵赖的实作。

实作

以下实作大量参考Golang RSA encrypt and decrypt exampleGolang: 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       : 程序码

keykey.pub是透过ssh-keygen这个软件来产生的,使用以下指令可以产生一组RSA公私钥:

$ ssh-keygen -t rsa -f <key's name> -m pem

e.g.

$ ssh-keygen -t rsa -f goodGuykey -m pem

输入後会询问是否要设定passphrase,这是一个安全密码,如果设定了,以後使用此私钥还要输入此安全密码才可使用,以增加安全性,此范例没有设定。

code 的方面主要可以看main的部分,注解有解释流程,搭配循序图会较好理解:

// 大量参考: https://gist.github.com/mfridman/c0c5ece512f63d429c4589196a1d4242
package main

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha512"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
)

// LoadFile load the file to bytes
func LoadFile(path string) []byte {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}

	return content
}

// BytesToPrivateKey bytes to private key
func BytesToPrivateKey(priv []byte) *rsa.PrivateKey {
	block, _ := pem.Decode(priv)
	enc := x509.IsEncryptedPEMBlock(block)
	b := block.Bytes
	var err error
	if 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 main

import (
	"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 := "小明餐点: 大冰红"

	// 小明利用与早餐店阿姨共同的钥匙产生HMAC
	requestHMAC := hmacSha256(meals, sharedSecret)

	// 坏人拦截小明的封包,利用自己的钥匙产生HMAC
	requestHMAC := hmacSha256(meals, badGuySecret)

	// 早餐店阿姨利用与小明共同的钥匙产生HMAC
	trueHMAC := 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/c0c5ece512f63d429c4589196a1d4242
package main

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha512"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
)

// LoadFile load the file to bytes
func LoadFile(path string) []byte {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}

	return content
}

// BytesToPrivateKey bytes to private key
func BytesToPrivateKey(priv []byte) *rsa.PrivateKey {
	block, _ := pem.Decode(priv)
	enc := x509.IsEncryptedPEMBlock(block)
	b := block.Bytes
	var err error
	if 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`利用小明的公钥验证後的确是小明用私钥签章的,故证明小明确实有点过餐
}

参考


<<:  [JS] You Don't Know JavaScript [this & Object Prototypes] - Prototypes [下]

>>:  【教练我想写 C#】建啦哪次不建 Web API # Get

Internxt Drive - 替代 Google Drive 免费 2 GB 云端空间服务,采用加密与分布式技术保护你的网路资料

Internxt Drive 是世界上最安全的云端储存服务之一,他采用客户端加密与分布布技术,使得所...

Day 8:AWS是什麽?30天从动漫/影视作品看AWS服务应用 -《PSYCHO-PASS心灵判官》part2

《PSYCHO-PASS心灵判官》中第三集监视官常守朱第二次遇到的事件,是「八王子工厂杀人事件」 在...

那些被忽略但很好用的 Web API / 结语

Web API -- Application Programming Interface for ...

【Day 29】Hook 09:自定义 Hook(Custom hook)

打造自己的 Hook 自 React 16.8 以後, 使用者就可以在 React 中 创建自定义的...

Powershell 入门之初识脚本

Powershell 的基础内容已经讲解的差不多了,从今天开始,我们将学习通过脚本来自动化执行批量操...