Skip to content

Instantly share code, notes, and snippets.

@lukehinds
Created March 24, 2023 13:43
Show Gist options
  • Save lukehinds/b521eea234e1c4be3332550304d5a75d to your computer and use it in GitHub Desktop.
Save lukehinds/b521eea234e1c4be3332550304d5a75d to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"flag"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
// Parse CLI arguments
filePath := flag.String("file", "", "Path to the file to sign")
flag.Parse()
// Load file contents
fileContent, err := ioutil.ReadFile(*filePath)
if err != nil {
panic(err)
}
// Compute file digest
hash := sha256.Sum256(fileContent)
// Generate RSA key pair
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
// Sign file digest
signature, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hash[:])
// Encode signature in base64
signatureB64 := base64.StdEncoding.EncodeToString(signature)
// Encode public key in PEM format
pubKeyPEM := &pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: x509.MarshalPKCS1PublicKey(&key.PublicKey),
}
pubKeyPEMBytes := pem.EncodeToMemory(pubKeyPEM)
pubKeyB64 := base64.StdEncoding.EncodeToString(pubKeyPEMBytes)
// Build JSON payload
payload := map[string]string{
"fileName": *filePath,
"fileDigest": fmt.Sprintf("%x", hash[:]),
"signature": signatureB64,
"publicKey": pubKeyB64,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
panic(err)
}
// Send payload to server
resp, err := http.Post("http://localhost:8080/verify", "application/json", bytes.NewReader(payloadBytes))
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Print server response
fmt.Println("Server response:", resp.Status)
}
package main
import (
"crypto"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
// Define HTTP handler for /signature endpoint
http.HandleFunc("/verify", func(w http.ResponseWriter, r *http.Request) {
// Parse JSON payload
payloadBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
var payload struct {
FileName string `json:"fileName"`
FileDigest string `json:"fileDigest"`
Signature string `json:"signature"`
PublicKey string `json:"publicKey"`
}
err = json.Unmarshal(payloadBytes, &payload)
if err != nil {
fmt.Println(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Decode public key from PEM format
pubKeyPEMBytes, err := base64.StdEncoding.DecodeString(payload.PublicKey)
if err != nil {
fmt.Println(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
block, _ := pem.Decode(pubKeyPEMBytes)
pubKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
fmt.Println(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Decode signature from base64
signature, err := base64.StdEncoding.DecodeString(payload.Signature)
if err != nil {
fmt.Println(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
hash, err := hex.DecodeString(payload.FileDigest)
if err != nil {
// handle error
}
err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hash, signature)
if err != nil {
fmt.Println("71: ", err)
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
// Signature is valid
fmt.Printf("File '%s' has a valid signature!\n", payload.FileName)
// Send success response
w.WriteHeader(http.StatusOK)
})
// Start HTTP server
http.ListenAndServe(":8080", nil)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment