pkiapi/internal/pki/certificate.go

237 lines
5.9 KiB
Go

package pki
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"time"
)
// Certificate représente un certificat X.509
type Certificate struct {
ID string
Subject string
Issuer string
NotBefore time.Time
NotAfter time.Time
PublicKey *rsa.PublicKey
PrivateKey *rsa.PrivateKey // Stocké pour les CAs
Cert *x509.Certificate
Revoked bool
IsCA bool // True si c'est une autorité de certification
}
// GenerateCertificate crée un nouveau certificat auto-signé
func GenerateCertificate(subject string, validityDays int) (*Certificate, error) {
// Générer une clé RSA
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
// Créer le certificat
serialNumber, _ := rand.Int(rand.Reader, big.NewInt(1000000))
notBefore := time.Now()
notAfter := notBefore.AddDate(0, 0, validityDays)
cert := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: subject,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
}
// Auto-signer le certificat
certBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &privateKey.PublicKey, privateKey)
if err != nil {
return nil, err
}
parsedCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, err
}
return &Certificate{
Subject: subject,
Issuer: subject,
NotBefore: notBefore,
NotAfter: notAfter,
PublicKey: &privateKey.PublicKey,
PrivateKey: privateKey,
Cert: parsedCert,
Revoked: false,
IsCA: false,
}, nil
}
// GenerateCA crée une nouvelle autorité de certification
func GenerateCA(subject string, validityDays int) (*Certificate, error) {
// Générer une clé RSA
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
// Créer le certificat CA
serialNumber, _ := rand.Int(rand.Reader, big.NewInt(1000000000))
notBefore := time.Now()
notAfter := notBefore.AddDate(0, 0, validityDays)
cert := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: subject,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
IsCA: true,
MaxPathLen: -1, // Pas de limite de chaîne
}
// Auto-signer le certificat CA
certBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &privateKey.PublicKey, privateKey)
if err != nil {
return nil, err
}
parsedCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, err
}
return &Certificate{
Subject: subject,
Issuer: subject,
NotBefore: notBefore,
NotAfter: notAfter,
PublicKey: &privateKey.PublicKey,
PrivateKey: privateKey,
Cert: parsedCert,
Revoked: false,
IsCA: true,
}, nil
}
// SignCertificate signe un certificat avec une CA
func SignCertificate(caCert *Certificate, subject string, validityDays int) (*Certificate, error) {
if !caCert.IsCA {
return nil, ErrNotACA
}
if caCert.PrivateKey == nil {
return nil, ErrMissingPrivateKey
}
// Générer une clé RSA pour le nouveau certificat
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
// Créer le certificat à signer
serialNumber, _ := rand.Int(rand.Reader, big.NewInt(1000000000))
notBefore := time.Now()
notAfter := notBefore.AddDate(0, 0, validityDays)
cert := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: subject,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
Issuer: caCert.Cert.Subject,
}
// Signer le certificat avec la clé privée de la CA
certBytes, err := x509.CreateCertificate(rand.Reader, cert, caCert.Cert, &privateKey.PublicKey, caCert.PrivateKey)
if err != nil {
return nil, err
}
parsedCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, err
}
return &Certificate{
Subject: subject,
Issuer: caCert.Subject,
NotBefore: notBefore,
NotAfter: notAfter,
PublicKey: &privateKey.PublicKey,
PrivateKey: privateKey,
Cert: parsedCert,
Revoked: false,
IsCA: false,
}, nil
}
// SignSubCA signe une CA intermédiaire avec une CA parent
func SignSubCA(parentCA *Certificate, subject string, validityDays int) (*Certificate, error) {
if !parentCA.IsCA {
return nil, ErrNotACA
}
if parentCA.PrivateKey == nil {
return nil, ErrMissingPrivateKey
}
// Générer une clé RSA pour la sub-CA
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
// Créer le certificat CA intermédiaire
serialNumber, _ := rand.Int(rand.Reader, big.NewInt(1000000000))
notBefore := time.Now()
notAfter := notBefore.AddDate(0, 0, validityDays)
subCA := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: subject,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
IsCA: true,
MaxPathLen: 0, // Limite la chaîne : cette CA ne peut pas signer d'autres CAs
Issuer: parentCA.Cert.Subject,
}
// Signer le certificat CA avec la clé privée du parent
certBytes, err := x509.CreateCertificate(rand.Reader, subCA, parentCA.Cert, &privateKey.PublicKey, parentCA.PrivateKey)
if err != nil {
return nil, err
}
parsedCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, err
}
return &Certificate{
Subject: subject,
Issuer: parentCA.Subject,
NotBefore: notBefore,
NotAfter: notAfter,
PublicKey: &privateKey.PublicKey,
PrivateKey: privateKey,
Cert: parsedCert,
Revoked: false,
IsCA: true,
}, nil
}