iT邦幫忙

0

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`利用小明的公鑰驗證後的確是小明用私鑰簽章的,故證明小明確實有點過餐
}

參考


尚未有邦友留言

立即登入留言