Skip to content

Instantly share code, notes, and snippets.

@CarsonSlovoka
Last active August 23, 2024 11:09
Show Gist options
  • Save CarsonSlovoka/1876a3ae7cd821a201d39aa96beccffe to your computer and use it in GitHub Desktop.
Save CarsonSlovoka/1876a3ae7cd821a201d39aa96beccffe to your computer and use it in GitHub Desktop.
GPG(GNU Privacy Guard)
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/binary"
"fmt"
"io"
"testing"
)
func aesEncrypt(key, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key) // key長度有限制必須為16, 24, 32. 對應 AES-128, AES-192, or AES-256
if err != nil {
return nil, err
}
// 整個密文的長度 = blockSize + len(plaintext)
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
// 生成block的內容
iv := ciphertext[:aes.BlockSize]
if _, err = io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream( // 會執行cfb.XORKeyStream, 以plaintext和提供的iv, 計算結果存到ciphertext[aes.BlockSize:]去
ciphertext[aes.BlockSize:], // 這是輸出。此輸出搭配真實的密鑰,才可以反算回來
plaintext,
)
return ciphertext, nil
}
func aesDecrypt(key, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(ciphertext) < aes.BlockSize {
return nil, fmt.Errorf("ciphertext too short")
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(ciphertext, ciphertext)
return ciphertext, nil
}
func GPGEncrypt(data []byte, publicKey *rsa.PublicKey, symmetricKey []byte) ([]byte, error) {
// 使用一個對稱式加密演算法來生成會話的密鑰,假設我們使用AES來加密
// 被加密起來的訊息
encryptData, err := aesEncrypt(symmetricKey, data)
if err != nil {
return nil, err
}
// 使用公鑰加密訊息
iHash := sha256.New()
// 用對方的公鑰對會話密鑰加簽, 對方可以透過此公鑰的對應的私鑰將其還原
sessionKeyBytes, err := rsa.EncryptOAEP(iHash, rand.Reader, publicKey, symmetricKey, nil)
if err != nil {
return nil, err
}
// 以下只是隨便模擬一個簡單的過程,主要是接收方有辦法識別加簽起來的內容與加簽起來的密鑰
type Header struct {
DataSize uint32
KeySize uint32
}
encryptedMessageBuf := bytes.NewBuffer(nil)
_ = binary.Write(encryptedMessageBuf, binary.BigEndian, &Header{
DataSize: uint32(len(encryptData)),
KeySize: uint32(len(sessionKeyBytes)),
})
encryptedMessageBuf.Write(encryptData)
encryptedMessageBuf.Write(sessionKeyBytes)
return encryptedMessageBuf.Bytes(), nil
}
func GPGDecrypt(privateKey *rsa.PrivateKey, encryptedMessage []byte) (orgData []byte, sessionKey []byte, err error) {
// 解析接收的資料內容
// 這邊只是模擬接收到的資料結構,實際上的資料結構更為複雜
type ReceiveData struct {
MsgSize uint32
KeySize uint32
Msg []byte
Key []byte
}
var msgSize uint32
msgSize = binary.BigEndian.Uint32(encryptedMessage) // 這是我們自己訂的結構,其一開始4byte表示訊息長度
var receive ReceiveData
receive.Msg = encryptedMessage[8 : 8+msgSize] // 8為表頭前8byte(msgSize, keySize)
receive.Key = encryptedMessage[8+msgSize:]
// 解出會話用的真實密鑰pairKey
sessionKey, err = rsa.DecryptOAEP(sha256.New(), rand.Reader, // 此為OAEP的特色,對方已經用己方提供的公鑰加密,用自己的私鑰能解密
privateKey, // 接收方的私鑰理論上只有自己才會有,因此只要沒有這個私鑰,就沒有辦法把會話密鑰還原
receive.Key, nil,
)
if err != nil {
return nil, nil, err
}
// 使用會話密鑰將內容還原
orgData, err = aesDecrypt(sessionKey, receive.Msg)
return orgData, sessionKey, nil
}
func Test_rsaOAEP(t *testing.T) {
// 模擬取得接收方的公鑰
receiverKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
receiverPublicKey := &receiverKey.PublicKey
// 加簽過程: 用公鑰加密
realMessage := []byte("這是一個秘密訊息")
// 模擬隨機生成對稱式密鑰
const defaultKeySize = 16 // 由於我們採用AES演算法,他的私鑰長度有限制,對應的長度分別為16, 24, 32對應AES-128, AES-192, or AES-256.
symmetricKey := make([]byte, defaultKeySize)
if _, err = rand.Read(symmetricKey); err != nil {
t.Fatal(err)
}
encryptedMessage, err := GPGEncrypt(realMessage, receiverPublicKey, symmetricKey)
if err != nil {
t.Fatal(err)
}
expectedMsg, symmetricKey2, err := GPGDecrypt(receiverKey, encryptedMessage)
if err != nil {
t.Fatal(err)
}
// 驗證
if !bytes.Equal(symmetricKey, symmetricKey2) {
t.Fatal("密鑰不同", err)
}
if !bytes.Equal(expectedMsg, realMessage) {
t.Fatal("內容不一致")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment