Files
mq/examples/generate_cert.go
2025-03-13 18:31:16 +05:45

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
}