HomeArtTechHackBlockchain

สร้าง Key pair เพื่อทำการ signing document signature ด้วย Go lang

By Khomkrid Lerdprasert
Published in Technology
March 13, 2024
1 min read
สร้าง Key pair เพื่อทำการ signing document signature ด้วย Go lang

วันนี้เราจะมาสร้าง Key pair เพื่อทำการ signing document signature ด้วย Go lang จาก อีกคน ส่งไปยังอีกคนนึงกัน

ซึ่ง flow การทำงานคือ นาย A. จะทำการสร้าง Key pair public & private key เพื่อ หลังจากนั้นจะนำ Private key เก็บไว้ที่ตนเองและ ส่ง Public key ไปยังนาย B

หลังจากนั้น นาย A จะทำการสร้างเอกสารบางอย่าง เพื่อ signing signature จาก Private key และนำ เอกสารพร้อม signature ไปให้นาย B

นาย B จะทำการตรวจสอบ signature จาก Public key ที่ได้มาจาก นาย A ว่าเอกสารนั้นถูกต้องหรือไม่

Flow
Flow

สร้าง Key pair

func GenerateKeyPair(fileKeyPairName string) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
logrus.Error("Failed to generate RSA key pair:", err)
return
}
privateKeyFile, err := os.Create(fmt.Sprintf("%s_private_key.pem", fileKeyPairName))
if err != nil {
logrus.Error("Failed to create private key file:", err)
return
}
defer privateKeyFile.Close()
privateKeyPEM := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}
err = pem.Encode(privateKeyFile, privateKeyPEM)
if err != nil {
logrus.Error("Failed to write private key to file:", err)
return
}
logrus.Println(fmt.Sprintf("Private key saved to %s_private_key.pem", fileKeyPairName))
publicKeyFile, err := os.Create(fmt.Sprintf("%s_public_key.pem", fileKeyPairName))
if err != nil {
logrus.Error("Failed to create public key file:", err)
return
}
defer publicKeyFile.Close()
publicKeyBytes := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
publicKeyPEM := &pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: publicKeyBytes,
}
err = pem.Encode(publicKeyFile, publicKeyPEM)
if err != nil {
logrus.Error("Failed to write public key to file:", err)
return
}
logrus.Println(fmt.Sprintf("Public key saved to %s_public_key.pem", fileKeyPairName))
}

สร้าง Signature จาก Private key

func GenerateSignature(file string, privateKeyPEMFile string, signatureFileName string) {
privateKeyPEM, err := os.ReadFile(privateKeyPEMFile)
if err != nil {
logrus.Error("Failed to read private key:", err)
return
}
block, _ := pem.Decode(privateKeyPEM)
if block == nil || block.Type != "RSA PRIVATE KEY" {
logrus.Error("Failed to decode PEM block containing private key")
return
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
logrus.Error("Failed to parse private key:", err)
return
}
data, err := os.ReadFile(file)
if err != nil {
logrus.Error("Failed to read file:", err)
return
}
hash := sha256.Sum256(data)
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
if err != nil {
logrus.Println("Failed to sign file:", err)
return
}
signatureFile, err := os.Create(signatureFileName)
if err != nil {
logrus.Error("Failed to create signature file:", err)
return
}
defer signatureFile.Close()
_, err = signatureFile.Write(signature)
if err != nil {
logrus.Error("Failed to write signature to file:", err)
return
}
logrus.Println(fmt.Sprintf("Signature saved to %s", signatureFileName))
}

จากนั้นนำ signature ไปให้นาย B ตรวจสอบ ด้วยการ verify signature ด้วย Public key

func VerifySignature(fileName, signatureFilename, publicKeyPEMFile string) {
publicKeyPEM, err := os.ReadFile(publicKeyPEMFile)
if err != nil {
logrus.Error("Failed to read public key:", err)
return
}
block, _ := pem.Decode(publicKeyPEM)
if block == nil || block.Type != "RSA PUBLIC KEY" {
logrus.Error("Failed to decode PEM block containing public key")
return
}
data, err := os.ReadFile(fileName)
if err != nil {
logrus.Error("Failed to read file:", err)
return
}
publicKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
logrus.Error("Failed to parse public key:", err)
return
}
signature, err := os.ReadFile(signatureFilename)
if err != nil {
logrus.Error("Failed to read signature:", err)
return
}
hash := sha256.Sum256(data)
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hash[:], signature)
if err != nil {
logrus.Error("Failed to verify signature:", err)
return
}
logrus.Println("Signature verified successfully")
}

จากนั้น เราจะลองนำ Cobra เพื่อทำการสร้าง CLI ให้เราสามารถใช้งานได้ง่ายขึ้น โดยทำการติดตั้ง Cobra ก่อน

go install github.com/spf13/cobra/cobra@latest

จากนั้นทำการ สร้าง CLI ด้วย Cobra

cobra-cli init --author "Khomkrid Lerdprasert [email protected]" --license apache

เราจะมาเริ่มทำการ generate key pair กันก่อน

cobra-cli add generate-key-pair

จากนั้นใน cmd เราจะได้ file ที่ชื่อว่า generate-key-pair.go มา และเราจะทำการเขียน code ในไฟล์นี้ เพื่อรับ input เป็นชื่อ file prefix ของ key pair

/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
// generateKeyPairCmd represents the generateKeyPair command
var generateKeyPairCmd = &cobra.Command{
Use: "generate-key-pair",
Short: "A command to generate a key pair.",
Long: `A command to generate a key pair.
This command is used to generate a key pair. It will generate a public and private key using the RSA algorithm. The key pair will be used to sign and verify digital signatures.`,
Run: func(cmd *cobra.Command, args []string) {
logrus.Println("Generating key pair...", fileName)
GenerateKeyPair(fileName)
},
}
func init() {
generateKeyPairCmd.Flags().StringVarP(&fileName, "file", "f", "key_pair", "The name of the file to save the key pair to.")
rootCmd.AddCommand(generateKeyPairCmd)
}
func GenerateKeyPair(fileKeyPairName string) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
logrus.Error("Failed to generate RSA key pair:", err)
return
}
privateKeyFile, err := os.Create(fmt.Sprintf("%s_private_key.pem", fileKeyPairName))
if err != nil {
logrus.Error("Failed to create private key file:", err)
return
}
defer privateKeyFile.Close()
privateKeyPEM := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}
err = pem.Encode(privateKeyFile, privateKeyPEM)
if err != nil {
logrus.Error("Failed to write private key to file:", err)
return
}
logrus.Println(fmt.Sprintf("Private key saved to %s_private_key.pem", fileKeyPairName))
publicKeyFile, err := os.Create(fmt.Sprintf("%s_public_key.pem", fileKeyPairName))
if err != nil {
logrus.Error("Failed to create public key file:", err)
return
}
defer publicKeyFile.Close()
publicKeyBytes := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
publicKeyPEM := &pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: publicKeyBytes,
}
err = pem.Encode(publicKeyFile, publicKeyPEM)
if err != nil {
logrus.Error("Failed to write public key to file:", err)
return
}
logrus.Println(fmt.Sprintf("Public key saved to %s_public_key.pem", fileKeyPairName))
}

จากนั้นเราจะทำการสร้าง command สำหรับการ generate signature จาก private key โดยจะรับ input เป็น file ที่จะทำการ sign และ private key ที่จะใช้ในการ sign และชื่อ file ที่จะเก็บ signature

cobra-cli add generate-signature

จากนั้นใน cmd เราจะได้ file ที่ชื่อว่า generate-signature.go มา และเราจะทำการเขียน code ในไฟล์นี้

/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
// generateSignatureCmd represents the generateSignature command
var generateSignatureCmd = &cobra.Command{
Use: "generate-signature",
Short: "A command to generate a digital signature.",
Long: `A command to generate a digital signature from a file.
This command is used to generate a digital signature from a file. It will use the private key to sign the file and generate a digital signature. The digital signature can be used to verify the integrity of the file.`,
Run: func(cmd *cobra.Command, args []string) {
logrus.Println("Generating signature...")
GenerateSignature(fileName, privateKey, signatureFileName)
},
}
func init() {
generateSignatureCmd.Flags().StringVarP(&fileName, "file", "f", "", "The name of the file to generate a digital signature for.")
generateSignatureCmd.Flags().StringVarP(&privateKey, "private-key", "p", "", "The private key to use to generate the digital signature.")
generateSignatureCmd.Flags().StringVarP(&signatureFileName, "signature-file", "s", "", "The name of the file to save the digital signature to.")
rootCmd.AddCommand(generateSignatureCmd)
}
func GenerateSignature(file string, privateKeyPEMFile string, signatureFileName string) {
privateKeyPEM, err := os.ReadFile(privateKeyPEMFile)
if err != nil {
logrus.Error("Failed to read private key:", err)
return
}
block, _ := pem.Decode(privateKeyPEM)
if block == nil || block.Type != "RSA PRIVATE KEY" {
logrus.Error("Failed to decode PEM block containing private key")
return
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
logrus.Error("Failed to parse private key:", err)
return
}
data, err := os.ReadFile(file)
if err != nil {
logrus.Error("Failed to read file:", err)
return
}
hash := sha256.Sum256(data)
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
if err != nil {
logrus.Println("Failed to sign file:", err)
return
}
signatureFile, err := os.Create(signatureFileName)
if err != nil {
logrus.Error("Failed to create signature file:", err)
return
}
defer signatureFile.Close()
_, err = signatureFile.Write(signature)
if err != nil {
logrus.Error("Failed to write signature to file:", err)
return
}
logrus.Println(fmt.Sprintf("Signature saved to %s", signatureFileName))
}

จากนั้นเราจะทำการสร้าง command สำหรับการ verify signature ด้วย public key โดย command นี้จะรับ input เป็น file ที่จะทำการ verify และ signature ที่จะใช้ในการ verify และ public key ที่จะใช้ในการ verify

cobra-cli add verifydata

จากนั้นใน cmd เราจะได้ file ที่ชื่อว่า verifydata.go มา และเราจะทำการเขียน code ในไฟล์นี้

/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"os"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
// verifydataCmd represents the verifydata command
var verifydataCmd = &cobra.Command{
Use: "verifydata",
Short: "A command to verify the integrity of a file.",
Long: `A command to verify the integrity of a file.
This command is used to verify the integrity of a file. It will use the public key to verify the digital signature and check the integrity of the file. The digital signature can be used to verify the integrity of the file.`,
Run: func(cmd *cobra.Command, args []string) {
logrus.Println("Verifying signature...")
VerifySignature(fileName, signatureFileName, publicKey)
},
}
func init() {
verifydataCmd.Flags().StringVarP(&fileName, "file", "f", "", "The name of the file to verify the integrity of.")
verifydataCmd.Flags().StringVarP(&signatureFileName, "signature-file", "s", "", "The name of the file containing the digital signature.")
verifydataCmd.Flags().StringVarP(&publicKey, "public-key", "p", "", "The public key to use to verify the digital signature.")
rootCmd.AddCommand(verifydataCmd)
}
func VerifySignature(fileName, signatureFilename, publicKeyPEMFile string) {
publicKeyPEM, err := os.ReadFile(publicKeyPEMFile)
if err != nil {
logrus.Error("Failed to read public key:", err)
return
}
block, _ := pem.Decode(publicKeyPEM)
if block == nil || block.Type != "RSA PUBLIC KEY" {
logrus.Error("Failed to decode PEM block containing public key")
return
}
data, err := os.ReadFile(fileName)
if err != nil {
logrus.Error("Failed to read file:", err)
return
}
publicKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
logrus.Error("Failed to parse public key:", err)
return
}
signature, err := os.ReadFile(signatureFilename)
if err != nil {
logrus.Error("Failed to read signature:", err)
return
}
hash := sha256.Sum256(data)
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hash[:], signature)
if err != nil {
logrus.Error("Failed to verify signature:", err)
return
}
logrus.Println("Signature verified successfully")
}

จากนั้นเราจะทำการสร้าง command สำหรับการทำการเรียกใช้งานทั้งหมด โดยเพิ่ม var ในการเรียกใช้งานจาก root cmd เข้าไป

var (
fileName string
privateKey string
publicKey string
signatureFileName string
)

เมื่อทดลอง run program จะได้ผลลัพธ์ดังนี้

digitalsigner is a CLI tool for signing and verifying digital signature.:
This application is a tool to sign and verify digital signature.
It is a CLI tool that can be used to sign and verify digital signature.
Usage:
digitalsigner [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
generate-key-pair A command to generate a key pair.
generate-signature A command to generate a digital signature.
help Help about any command
verifydata A command to verify the integrity of a file.
Flags:
-h, --help help for digitalsigner
-t, --toggle Help message for toggle
Use "digitalsigner [command] --help" for more information about a command.

โค้ดทั้งหมดสามารถดูได้ที่ https://github.com/aofiee/digitalsigner.git


Tags

#Go lang

Share

Previous Article
Burp Suite for IOS
Khomkrid Lerdprasert

Khomkrid Lerdprasert

Full Stack Life

Table Of Contents

1
วันนี้เราจะมาสร้าง Key pair เพื่อทำการ signing document signature ด้วย Go lang จาก อีกคน ส่งไปยังอีกคนนึงกัน
2
สร้าง Key pair
3
สร้าง Signature จาก Private key

Related Posts

บันทึก การทำ Firebase Messaging ด้วย Golang และ Config ให้ใช้งานบน iOS
November 26, 2021
1 min
© 2024, All Rights Reserved.
Powered By

Quick Links

Author

Social Media