mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-05 16:06:55 +08:00
469 lines
13 KiB
Go
469 lines
13 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/ed25519"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"math/big"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/oarkflow/json"
|
|
)
|
|
|
|
func main() {
|
|
|
|
caCertDER, caPrivateKey := generateCA("P384", "My Secure CA")
|
|
saveCertificate("ca.crt", caCertDER)
|
|
signFileContent("ca.crt", caPrivateKey)
|
|
|
|
caCert, err := x509.ParseCertificate(caCertDER)
|
|
if err != nil {
|
|
log.Fatal("Failed to parse CA certificate:", err)
|
|
}
|
|
|
|
serverCertDER, serverKey := generateServerCert(caCert, caPrivateKey, "P256", "server.example.com",
|
|
[]string{"localhost", "server.example.com"}, []net.IP{net.ParseIP("127.0.0.1")})
|
|
saveCertificate("server.crt", serverCertDER)
|
|
signFileContent("server.crt", caPrivateKey)
|
|
|
|
clientCertDER, clientKey := generateClientCert(caCert, caPrivateKey, "client-user")
|
|
saveCertificate("client.crt", clientCertDER)
|
|
signFileContent("client.crt", caPrivateKey)
|
|
|
|
codeSignCertDER, codeSignKey := generateCodeSigningCert(caCert, caPrivateKey, 2048, "file-signer")
|
|
saveCertificate("code_sign.crt", codeSignCertDER)
|
|
signFileContent("code_sign.crt", caPrivateKey)
|
|
|
|
revokedCerts := []pkix.RevokedCertificate{
|
|
{SerialNumber: big.NewInt(1), RevocationTime: time.Now()},
|
|
}
|
|
generateCRL(caCert, caPrivateKey, revokedCerts)
|
|
|
|
if err := validateClientCert("client.crt", "ca.crt"); err != nil {
|
|
log.Fatal("Client certificate validation failed:", err)
|
|
}
|
|
|
|
log.Println("All certificates generated and signed successfully.")
|
|
|
|
dss := NewDigitalSignatureService(clientKey)
|
|
|
|
plainText := "The quick brown fox jumps over the lazy dog."
|
|
textSig, err := dss.SignText(plainText)
|
|
if err != nil {
|
|
log.Fatal("Failed to sign plain text:", err)
|
|
}
|
|
if err := dss.VerifyText(plainText, textSig); err != nil {
|
|
log.Fatal("Plain text signature verification failed:", err)
|
|
}
|
|
log.Println("Plain text signature verified.")
|
|
|
|
jsonData := map[string]interface{}{
|
|
"name": "Alice",
|
|
"age": 30,
|
|
"premium": true,
|
|
}
|
|
jsonSig, err := dss.SignJSON(jsonData)
|
|
if err != nil {
|
|
log.Fatal("Failed to sign JSON data:", err)
|
|
}
|
|
fmt.Println(string(jsonSig))
|
|
if err := dss.VerifyJSON(jsonData, jsonSig); err != nil {
|
|
log.Fatal("JSON signature verification failed:", err)
|
|
}
|
|
log.Println("JSON signature verified.")
|
|
|
|
_ = serverKey
|
|
_ = codeSignKey
|
|
}
|
|
|
|
type DigitalSignatureService struct {
|
|
Signer crypto.Signer
|
|
PublicKey crypto.PublicKey
|
|
}
|
|
|
|
func NewDigitalSignatureService(signer crypto.Signer) *DigitalSignatureService {
|
|
return &DigitalSignatureService{
|
|
Signer: signer,
|
|
PublicKey: signer.Public(),
|
|
}
|
|
}
|
|
|
|
func (dss *DigitalSignatureService) SignData(data []byte) ([]byte, error) {
|
|
switch dss.Signer.(type) {
|
|
case ed25519.PrivateKey:
|
|
return dss.Signer.Sign(rand.Reader, data, crypto.Hash(0))
|
|
default:
|
|
h := sha256.New()
|
|
h.Write(data)
|
|
hashed := h.Sum(nil)
|
|
return dss.Signer.Sign(rand.Reader, hashed, crypto.SHA256)
|
|
}
|
|
}
|
|
|
|
func (dss *DigitalSignatureService) VerifyData(data, signature []byte) error {
|
|
switch pub := dss.PublicKey.(type) {
|
|
case ed25519.PublicKey:
|
|
if ed25519.Verify(pub, data, signature) {
|
|
return nil
|
|
}
|
|
return errors.New("ed25519 signature verification failed")
|
|
case *rsa.PublicKey:
|
|
h := sha256.New()
|
|
h.Write(data)
|
|
hashed := h.Sum(nil)
|
|
return rsa.VerifyPKCS1v15(pub, crypto.SHA256, hashed, signature)
|
|
case *ecdsa.PublicKey:
|
|
h := sha256.New()
|
|
h.Write(data)
|
|
hashed := h.Sum(nil)
|
|
var sig struct {
|
|
R, S *big.Int
|
|
}
|
|
if _, err := asn1.Unmarshal(signature, &sig); err != nil {
|
|
return err
|
|
}
|
|
if ecdsa.Verify(pub, hashed, sig.R, sig.S) {
|
|
return nil
|
|
}
|
|
return errors.New("ecdsa signature verification failed")
|
|
default:
|
|
return errors.New("unsupported public key type")
|
|
}
|
|
}
|
|
|
|
func (dss *DigitalSignatureService) SignText(text string) ([]byte, error) {
|
|
return dss.SignData([]byte(text))
|
|
}
|
|
|
|
func (dss *DigitalSignatureService) VerifyText(text string, signature []byte) error {
|
|
return dss.VerifyData([]byte(text), signature)
|
|
}
|
|
|
|
func (dss *DigitalSignatureService) SignJSON(v interface{}) ([]byte, error) {
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return dss.SignData(b)
|
|
}
|
|
|
|
func (dss *DigitalSignatureService) VerifyJSON(v interface{}, signature []byte) error {
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return dss.VerifyData(b, signature)
|
|
}
|
|
|
|
func generateCA(curveName string, commonName string) ([]byte, crypto.Signer) {
|
|
privKey, err := generatePrivateKey("ECDSA", curveName)
|
|
if err != nil {
|
|
log.Fatal("CA key generation failed:", err)
|
|
}
|
|
subject := pkix.Name{
|
|
CommonName: commonName,
|
|
Organization: []string{"Secure CA Org"},
|
|
Country: []string{"US"},
|
|
}
|
|
template := x509.Certificate{
|
|
SerialNumber: randomSerial(),
|
|
Subject: subject,
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().AddDate(10, 0, 0),
|
|
IsCA: true,
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
BasicConstraintsValid: true,
|
|
MaxPathLenZero: true,
|
|
}
|
|
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, privKey.Public(), privKey)
|
|
if err != nil {
|
|
log.Fatal("CA cert creation failed:", err)
|
|
}
|
|
|
|
return certBytes, privKey
|
|
}
|
|
|
|
func generateServerCert(caCert *x509.Certificate, caKey crypto.Signer, keyType string, commonName string, dnsNames []string, ips []net.IP) ([]byte, crypto.Signer) {
|
|
privKey, err := generatePrivateKey("ECDSA", keyType)
|
|
if err != nil {
|
|
log.Fatal("Server key generation failed:", err)
|
|
}
|
|
template := x509.Certificate{
|
|
SerialNumber: randomSerial(),
|
|
Subject: pkix.Name{
|
|
CommonName: commonName,
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().AddDate(1, 0, 0),
|
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
DNSNames: dnsNames,
|
|
IPAddresses: ips,
|
|
}
|
|
certBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, privKey.Public(), caKey)
|
|
if err != nil {
|
|
log.Fatal("Server cert creation failed:", err)
|
|
}
|
|
return certBytes, privKey
|
|
}
|
|
|
|
func generateClientCert(caCert *x509.Certificate, caKey crypto.Signer, commonName string) ([]byte, crypto.Signer) {
|
|
privKey, err := generatePrivateKey("Ed25519", nil)
|
|
if err != nil {
|
|
log.Fatal("Client key generation failed:", err)
|
|
}
|
|
template := x509.Certificate{
|
|
SerialNumber: randomSerial(),
|
|
Subject: pkix.Name{
|
|
CommonName: commonName,
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().AddDate(1, 0, 0),
|
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}
|
|
certBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, privKey.Public(), caKey)
|
|
if err != nil {
|
|
log.Fatal("Client cert creation failed:", err)
|
|
}
|
|
return certBytes, privKey
|
|
}
|
|
|
|
func generateCodeSigningCert(caCert *x509.Certificate, caKey crypto.Signer, rsaBits int, commonName string) ([]byte, crypto.Signer) {
|
|
privKey, err := generatePrivateKey("RSA", rsaBits)
|
|
if err != nil {
|
|
log.Fatal("Code signing key generation failed:", err)
|
|
}
|
|
template := x509.Certificate{
|
|
SerialNumber: randomSerial(),
|
|
Subject: pkix.Name{
|
|
CommonName: commonName,
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().AddDate(5, 0, 0),
|
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
|
|
}
|
|
certBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, privKey.Public(), caKey)
|
|
if err != nil {
|
|
log.Fatal("Code signing cert creation failed:", err)
|
|
}
|
|
return certBytes, privKey
|
|
}
|
|
|
|
func generateCRL(caCert *x509.Certificate, caKey crypto.Signer, revoked []pkix.RevokedCertificate) {
|
|
crlTemplate := &x509.RevocationList{
|
|
SignatureAlgorithm: caCert.SignatureAlgorithm,
|
|
RevokedCertificates: revoked,
|
|
Number: randomSerial(),
|
|
ThisUpdate: time.Now(),
|
|
NextUpdate: time.Now().AddDate(0, 1, 0),
|
|
Issuer: caCert.Subject,
|
|
}
|
|
crlBytes, err := x509.CreateRevocationList(rand.Reader, crlTemplate, caCert, caKey)
|
|
if err != nil {
|
|
log.Fatal("CRL creation failed:", err)
|
|
}
|
|
saveCRL("ca.crl", crlBytes)
|
|
signFileContent("ca.crl", caKey)
|
|
}
|
|
|
|
func generatePrivateKey(algo string, param interface{}) (crypto.Signer, error) {
|
|
switch algo {
|
|
case "RSA":
|
|
bits, ok := param.(int)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid RSA parameter")
|
|
}
|
|
return rsa.GenerateKey(rand.Reader, bits)
|
|
case "ECDSA":
|
|
curveName, ok := param.(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid ECDSA parameter")
|
|
}
|
|
var curve elliptic.Curve
|
|
switch curveName {
|
|
case "P224":
|
|
curve = elliptic.P224()
|
|
case "P256":
|
|
curve = elliptic.P256()
|
|
case "P384":
|
|
curve = elliptic.P384()
|
|
case "P521":
|
|
curve = elliptic.P521()
|
|
default:
|
|
return nil, fmt.Errorf("unsupported curve")
|
|
}
|
|
return ecdsa.GenerateKey(curve, rand.Reader)
|
|
case "Ed25519":
|
|
_, priv, err := ed25519.GenerateKey(rand.Reader)
|
|
return priv, err
|
|
default:
|
|
return nil, fmt.Errorf("unsupported algorithm")
|
|
}
|
|
}
|
|
|
|
func saveCertificate(filename string, cert []byte) {
|
|
err := os.WriteFile(filename, pem.EncodeToMemory(&pem.Block{
|
|
Type: "CERTIFICATE",
|
|
Bytes: cert,
|
|
}), 0644)
|
|
if err != nil {
|
|
log.Fatal("Failed to save certificate:", err)
|
|
}
|
|
}
|
|
|
|
func savePrivateKey(filename string, key crypto.Signer) {
|
|
keyBytes, err := x509.MarshalPKCS8PrivateKey(key)
|
|
if err != nil {
|
|
log.Fatal("Failed to marshal private key:", err)
|
|
}
|
|
err = os.WriteFile(filename, pem.EncodeToMemory(&pem.Block{
|
|
Type: "PRIVATE KEY",
|
|
Bytes: keyBytes,
|
|
}), 0600)
|
|
if err != nil {
|
|
log.Fatal("Failed to save private key:", err)
|
|
}
|
|
}
|
|
|
|
func saveCRL(filename string, crl []byte) {
|
|
err := os.WriteFile(filename, pem.EncodeToMemory(&pem.Block{
|
|
Type: "X509 CRL",
|
|
Bytes: crl,
|
|
}), 0644)
|
|
if err != nil {
|
|
log.Fatal("Failed to save CRL:", err)
|
|
}
|
|
}
|
|
|
|
func signFileContent(filename string, signer crypto.Signer) {
|
|
data, err := os.ReadFile(filename)
|
|
if err != nil {
|
|
log.Fatal("Failed to read file for signing:", err)
|
|
}
|
|
sig, err := signData(data, signer)
|
|
if err != nil {
|
|
log.Fatal("Failed to sign file content:", err)
|
|
}
|
|
err = os.WriteFile(filename+".sig", sig, 0644)
|
|
if err != nil {
|
|
log.Fatal("Failed to save signature file:", err)
|
|
}
|
|
}
|
|
|
|
func signData(data []byte, key crypto.Signer) ([]byte, error) {
|
|
switch key.(type) {
|
|
case ed25519.PrivateKey:
|
|
return key.Sign(rand.Reader, data, crypto.Hash(0))
|
|
default:
|
|
h := sha256.New()
|
|
h.Write(data)
|
|
hashed := h.Sum(nil)
|
|
return key.Sign(rand.Reader, hashed, crypto.SHA256)
|
|
}
|
|
}
|
|
|
|
func verifyDataSignature(data, signature []byte, pub crypto.PublicKey) error {
|
|
switch pub := pub.(type) {
|
|
case ed25519.PublicKey:
|
|
if ed25519.Verify(pub, data, signature) {
|
|
return nil
|
|
}
|
|
return errors.New("ed25519 signature verification failed")
|
|
case *rsa.PublicKey:
|
|
h := sha256.New()
|
|
h.Write(data)
|
|
hashed := h.Sum(nil)
|
|
return rsa.VerifyPKCS1v15(pub, crypto.SHA256, hashed, signature)
|
|
case *ecdsa.PublicKey:
|
|
h := sha256.New()
|
|
h.Write(data)
|
|
hashed := h.Sum(nil)
|
|
var sig struct {
|
|
R, S *big.Int
|
|
}
|
|
if _, err := asn1.Unmarshal(signature, &sig); err != nil {
|
|
return err
|
|
}
|
|
if ecdsa.Verify(pub, hashed, sig.R, sig.S) {
|
|
return nil
|
|
}
|
|
return errors.New("ecdsa signature verification failed")
|
|
default:
|
|
return errors.New("unsupported public key type")
|
|
}
|
|
}
|
|
|
|
func verifyFileContentSignature(filename, sigFilename string, pub crypto.PublicKey) error {
|
|
data, err := os.ReadFile(filename)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read file: %w", err)
|
|
}
|
|
sig, err := os.ReadFile(sigFilename)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read signature file: %w", err)
|
|
}
|
|
return verifyDataSignature(data, sig, pub)
|
|
}
|
|
|
|
func validateClientCert(clientCertPath, caCertPath string) error {
|
|
caCertBytes, err := os.ReadFile(caCertPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
block, _ := pem.Decode(caCertBytes)
|
|
if block == nil {
|
|
return fmt.Errorf("failed to decode CA cert PEM")
|
|
}
|
|
caCert, err := x509.ParseCertificate(block.Bytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
clientCertBytes, err := os.ReadFile(clientCertPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
block, _ = pem.Decode(clientCertBytes)
|
|
if block == nil {
|
|
return fmt.Errorf("failed to decode client cert PEM")
|
|
}
|
|
clientCert, err := x509.ParseCertificate(block.Bytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
opts := x509.VerifyOptions{
|
|
Roots: x509.NewCertPool(),
|
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}
|
|
opts.Roots.AddCert(caCert)
|
|
if _, err = clientCert.Verify(opts); err != nil {
|
|
return fmt.Errorf("certificate chain verification failed: %w", err)
|
|
}
|
|
if err = verifyFileContentSignature(clientCertPath, clientCertPath+".sig", caCert.PublicKey); err != nil {
|
|
return fmt.Errorf("file signature verification failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func randomSerial() *big.Int {
|
|
serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
|
if err != nil {
|
|
log.Fatal("failed to generate serial number:", err)
|
|
}
|
|
return serial
|
|
}
|