golang practice - digital signature and authentication

I Digital signature

Imagine a scenario: Alice sends Bob a message (I'll invite you to dinner tomorrow). The message is encrypted with Bob's public key (public key encryption can ensure that only Bob can decrypt the message with his own private key after it is intercepted). However, since Bob's public key can be owned by others, Tom can also encrypt the information with Bob's public key and send it to Bob, Therefore, Bob cannot ensure that this message is sent by Alice herself, but if each message is signed by Alice herself, Bob will know that it is really sent by Alice after receiving the message, and can also prevent Alice from denying the message through a third-party organization.

Digital signature means that the sender signs the message. The specific method is to first calculate the hash value of the message, and then use the sender's private key to asymmetrically encrypt the hash value to obtain the digital signature. The message to be sent is sent together with the digital signature. After receiving the message, the receiver asymmetrically decrypts the digital signature with the sender's public key to obtain the hash value, then hashes the message to obtain the hash value, and compares the decrypted hash value with the hash value. If the two are equal, it indicates that the message is indeed sent by the sender.

RSA and elliptic curve are commonly used for digital signature.

II golang actual combat

1. Digital signature and authentication using rsa

crypto/rsa package provides SignPKCS1v15 method for digital signature and VerifyPKCS1v15 method for digital signature authentication.

The steps of using rsa private key file for digital signature are as follows.

(1) Read the contents of the private key file and use pem Decode parses the content into pem format blocks

(2) Through x509 Parsepkcs1privatekey will PKCs # 1, ASN in pem block 1. Parse the string in der format into RSA PrivateKey

(3) Calculate send content hash value

(4) Via RSA Signpkcs1v15 uses the private key to sign the hash value. The return value of this function is the content of the digital signature

The steps of using rsa public key file for authentication digital signature are as follows.

(1) Read the contents of the public key file and use pem Decode parses the content into pem format blocks

(2) Through x509 Parsepkixpublickey parses the DER format string in the pem block into RSA PublicKey

(3) Calculate the hash value of the received content

(4) Via RSA Verifypkcs1v15 uses the public key to authenticate the hash value. If the return err == nil, the authentication is successful

package main

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha512"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"
)

func rsaSignature(plainText []byte, privateKeyFile string) ([]byte, error) {
	// Read the private key file and parse it into RSA PrivateKey
	file, err := os.Open(privateKeyFile)
	if err != nil {
		return nil, err
	}
	stat, err := file.Stat()
	if err != nil {
		return nil, err
	}
	buf := make([]byte, stat.Size())
	file.Read(buf)
	defer file.Close()

	block, _ := pem.Decode(buf)
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)

	// Calculates the hash value of the original content
	h := sha512.New()
	h.Write(plainText)
	hValue := h.Sum(nil)

	// Via RSA Signpkcs1v15 uses the private key to sign the original content hash value
	digestSign, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA512, hValue)
	return digestSign, err
}

func rsaVerifySign(plainText []byte, publicKeyFile string, signed []byte) bool {
	// Read the public key file and parse it into RSA PublicKey
	file, err := os.Open(publicKeyFile)
	if err != nil {
		return false
	}
	stat, err := file.Stat()
	if err != nil {
		return false
	}
	buf := make([]byte, stat.Size())
	file.Read(buf)
	defer file.Close()

	block, _ := pem.Decode(buf)
	publicKeyInt, err := x509.ParsePKIXPublicKey(block.Bytes)
	publicKey := publicKeyInt.(*rsa.PublicKey)

	// Calculates the hash value of the original content
	h := sha512.New()
	h.Write(plainText)
	hValue := h.Sum(nil)

	// witnessing
	err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA512,hValue, signed)

	return err == nil
}

func main() {
	content := []byte("Hello digest sign")
	sign, err := rsaSignature(content, "private.pem")
	if err != nil {
		return
	}
	fmt.Println("signature:",sign)
	fmt.Println("verify result:", rsaVerifySign(content, "public.pem", sign))
}

The operation results are as follows.  

2. Digital signature and authentication using elliptic curve

package main

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha512"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"
)

func eccSignature(plainText []byte, privateKeyFile string) ([]byte, []byte, error){
	// Read the private key file and parse it into ECDSA PrivateKey
	file, err := os.Open(privateKeyFile)
	if err != nil {
		return nil, nil, err
	}
	stat, err := file.Stat()
	if err != nil {
		return nil, nil, err
	}
	buf := make([]byte, stat.Size())
	file.Read(buf)
	defer file.Close()

	block, _ := pem.Decode(buf)
	privateKey, _ := x509.ParseECPrivateKey(block.Bytes)

	// Calculates the hash value of the original content
	h := sha512.New()
	h.Write(plainText)
	hValue := h.Sum(nil)

	r, s, err := ecdsa.Sign(rand.Reader, privateKey, hValue)
	rText, _ := r.MarshalText()
	sText, _ := s.MarshalText()

	return rText, sText, nil
}

func eccVerifySign(plainText []byte, publicKeyFile string, rText []byte, sText []byte) bool {
	// Read the public key file and parse it into ECDSA PublicKey
	file, err := os.Open(publicKeyFile)
	if err != nil {
		return false
	}
	stat, err := file.Stat()
	if err != nil {
		return false
	}
	buf := make([]byte, stat.Size())
	file.Read(buf)
	defer file.Close()

	block, _ := pem.Decode(buf)
	publicKeyInt, err := x509.ParsePKIXPublicKey(block.Bytes)
	publicKey := publicKeyInt.(*ecdsa.PublicKey)

	// Calculates the hash value of the original content
	h := sha512.New()
	h.Write(plainText)
	hValue := h.Sum(nil)

	var r, s big.Int
	r.UnmarshalText(rText)
	s.UnmarshalText(sText)

	return ecdsa.Verify(publicKey, hValue, &r, &s)
}

func main() {
	r, s, err := eccSignature(content, "ecc_private.pem")
	if err != nil {
		return
	}
	fmt.Println("r:", string(r))
	fmt.Println("s:", string(s))
	fmt.Println("verify result:", eccVerifySign(content, "ecc_public.pem", r, s))
}

The operation results are as follows.  

III Problems in digital signature

One end of the verification signature cannot confirm whether the public key really belongs to the sender. As mentioned above, Tom, the middleman, intercepted Alice's public key and sent it to Bob with his own public key. Then Tom intercepted Alice's data, generated the data and signed it himself, but Bob could not know that the public key had been stolen.

This problem can be solved by using certificates.

Keywords: Go security

Added by ann on Sun, 09 Jan 2022 09:00:48 +0200