
- Add CertificateAuthorityService for CA management - Support creation of root and intermediate CAs - Implement CA certificate generation with proper X.509 attributes - Add CA validation and verification functionality - Include comprehensive test coverage for CA operations - Support multiple CA types and configurations - Add proper error handling and logging
638 lines
19 KiB
Go
638 lines
19 KiB
Go
package certificate
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"git.secnex.io/secnex/certman/certificate/utils"
|
|
"git.secnex.io/secnex/certman/models"
|
|
"git.secnex.io/secnex/certman/repositories"
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// CertificateAuthorityService handles certificate authority operations
|
|
type CertificateAuthorityService struct {
|
|
caRepo *repositories.CertificateAuthorityRepository
|
|
orgRepo *repositories.OrganizationRepository
|
|
certDir string
|
|
privateDir string
|
|
}
|
|
|
|
// NewCertificateAuthorityService creates a new certificate authority service
|
|
func NewCertificateAuthorityService(
|
|
db *gorm.DB,
|
|
certDir string,
|
|
privateDir string,
|
|
) *CertificateAuthorityService {
|
|
return &CertificateAuthorityService{
|
|
caRepo: repositories.NewCertificateAuthorityRepository(db),
|
|
orgRepo: repositories.NewOrganizationRepository(db),
|
|
certDir: certDir,
|
|
privateDir: privateDir,
|
|
}
|
|
}
|
|
|
|
// CreateRootCA creates a new root certificate authority
|
|
func (s *CertificateAuthorityService) CreateRootCA(req *CreateRootCARequest) (*models.CertificateAuthority, error) {
|
|
// Validate request
|
|
if err := s.validateRootCARequest(req); err != nil {
|
|
return nil, fmt.Errorf("invalid request: %w", err)
|
|
}
|
|
|
|
// Check if organization exists
|
|
org, err := s.orgRepo.GetByID(req.OrganizationID.String())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("organization not found: %w", err)
|
|
}
|
|
|
|
// Check if root CA already exists for this organization
|
|
existingCAs, err := s.caRepo.GetAll()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to check existing CAs: %w", err)
|
|
}
|
|
|
|
for _, ca := range existingCAs {
|
|
if ca.Root && ca.OrganizationID == req.OrganizationID {
|
|
return nil, fmt.Errorf("root CA already exists for organization %s", org.Name)
|
|
}
|
|
}
|
|
|
|
// Create certificate configuration
|
|
var config *utils.CertificateConfig
|
|
if req.ValidityYears > 0 {
|
|
config = utils.CACertificateConfigWithValidity(true, req.ValidityYears) // true = isRoot
|
|
} else {
|
|
config = utils.CACertificateConfig(true) // true = isRoot
|
|
}
|
|
config.CommonName = req.CommonName
|
|
config.Organization = org.Name
|
|
config.OrganizationalUnit = req.OrganizationalUnit
|
|
config.Country = req.Country
|
|
config.State = req.State
|
|
config.Locality = req.Locality
|
|
config.Street = req.Street
|
|
config.PostalCode = req.PostalCode
|
|
config.Email = req.Email
|
|
|
|
// Validate configuration
|
|
if err := utils.ValidateCertificateConfig(config); err != nil {
|
|
return nil, fmt.Errorf("invalid certificate configuration: %w", err)
|
|
}
|
|
|
|
// Generate certificate and private key
|
|
generator := utils.NewCertificateGenerator(config)
|
|
cert, privateKey, err := generator.GenerateSelfSignedCertificate()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate root CA certificate: %w", err)
|
|
}
|
|
|
|
// Save certificate and private key to files
|
|
certFileID, err := s.saveCertificate(cert, "root-ca")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to save certificate: %w", err)
|
|
}
|
|
|
|
privateKeyFileID, err := s.savePrivateKey(privateKey, "root-ca")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to save private key: %w", err)
|
|
}
|
|
|
|
// Create CA model
|
|
ca := &models.CertificateAuthority{
|
|
Name: req.Name,
|
|
Description: req.Description,
|
|
SerialNumber: cert.SerialNumber.String(),
|
|
AttributeCommonName: cert.Subject.CommonName,
|
|
AttributeOrganization: cert.Subject.Organization[0],
|
|
AttributeOrganizationUnit: cert.Subject.OrganizationalUnit[0],
|
|
AttributeCountry: cert.Subject.Country[0],
|
|
AttributeState: cert.Subject.Province[0],
|
|
AttributeLocality: cert.Subject.Locality[0],
|
|
AttributeStreet: cert.Subject.StreetAddress[0],
|
|
AttributeEmail: req.Email,
|
|
AttributeAddress: req.Address,
|
|
AttributePostalCode: cert.Subject.PostalCode[0],
|
|
AttributeNotBefore: cert.NotBefore,
|
|
AttributeNotAfter: cert.NotAfter,
|
|
Root: true,
|
|
ParentID: nil, // Root CA has no parent
|
|
OrganizationID: req.OrganizationID,
|
|
FileID: certFileID,
|
|
PrivateKeyID: privateKeyFileID,
|
|
}
|
|
|
|
// Save to database
|
|
createdCA, err := s.caRepo.Create(*ca)
|
|
if err != nil {
|
|
// Clean up files if database save fails
|
|
s.cleanupFiles(certFileID, privateKeyFileID)
|
|
return nil, fmt.Errorf("failed to save CA to database: %w", err)
|
|
}
|
|
|
|
return &createdCA, nil
|
|
}
|
|
|
|
// CreateIntermediateCA creates a new intermediate certificate authority
|
|
func (s *CertificateAuthorityService) CreateIntermediateCA(req *CreateIntermediateCARequest) (*models.CertificateAuthority, error) {
|
|
// Validate request
|
|
if err := s.validateIntermediateCARequest(req); err != nil {
|
|
return nil, fmt.Errorf("invalid request: %w", err)
|
|
}
|
|
|
|
// Check if organization exists
|
|
org, err := s.orgRepo.GetByID(req.OrganizationID.String())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("organization not found: %w", err)
|
|
}
|
|
|
|
// Get parent CA
|
|
parentCA, err := s.caRepo.GetByID(req.ParentCAID.String())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parent CA not found: %w", err)
|
|
}
|
|
|
|
// Load parent CA certificate and private key
|
|
parentCert, parentPrivateKey, err := s.loadCACertificateAndKey(&parentCA)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load parent CA certificate: %w", err)
|
|
}
|
|
|
|
// Create certificate configuration
|
|
var config *utils.CertificateConfig
|
|
if req.ValidityYears > 0 {
|
|
config = utils.CACertificateConfigWithValidity(false, req.ValidityYears) // false = not root
|
|
} else {
|
|
config = utils.CACertificateConfig(false) // false = not root
|
|
}
|
|
config.CommonName = req.CommonName
|
|
config.Organization = org.Name
|
|
config.OrganizationalUnit = req.OrganizationalUnit
|
|
config.Country = req.Country
|
|
config.State = req.State
|
|
config.Locality = req.Locality
|
|
config.Street = req.Street
|
|
config.PostalCode = req.PostalCode
|
|
config.Email = req.Email
|
|
|
|
// Validate configuration
|
|
if err := utils.ValidateCertificateConfig(config); err != nil {
|
|
return nil, fmt.Errorf("invalid certificate configuration: %w", err)
|
|
}
|
|
|
|
// Generate certificate and private key
|
|
generator := utils.NewCertificateGenerator(config)
|
|
cert, privateKey, err := generator.GenerateCertificate(parentCert, parentPrivateKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate intermediate CA certificate: %w", err)
|
|
}
|
|
|
|
// Save certificate and private key to files
|
|
certFileID, err := s.saveCertificate(cert, "intermediate-ca")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to save certificate: %w", err)
|
|
}
|
|
|
|
privateKeyFileID, err := s.savePrivateKey(privateKey, "intermediate-ca")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to save private key: %w", err)
|
|
}
|
|
|
|
// Create CA model
|
|
ca := &models.CertificateAuthority{
|
|
Name: req.Name,
|
|
Description: req.Description,
|
|
SerialNumber: cert.SerialNumber.String(),
|
|
AttributeCommonName: cert.Subject.CommonName,
|
|
AttributeOrganization: cert.Subject.Organization[0],
|
|
AttributeOrganizationUnit: cert.Subject.OrganizationalUnit[0],
|
|
AttributeCountry: cert.Subject.Country[0],
|
|
AttributeState: cert.Subject.Province[0],
|
|
AttributeLocality: cert.Subject.Locality[0],
|
|
AttributeStreet: cert.Subject.StreetAddress[0],
|
|
AttributeEmail: req.Email,
|
|
AttributeAddress: req.Address,
|
|
AttributePostalCode: cert.Subject.PostalCode[0],
|
|
AttributeNotBefore: cert.NotBefore,
|
|
AttributeNotAfter: cert.NotAfter,
|
|
Root: false,
|
|
ParentID: &req.ParentCAID,
|
|
OrganizationID: req.OrganizationID,
|
|
FileID: certFileID,
|
|
PrivateKeyID: privateKeyFileID,
|
|
}
|
|
|
|
// Save to database
|
|
createdCA, err := s.caRepo.Create(*ca)
|
|
if err != nil {
|
|
// Clean up files if database save fails
|
|
s.cleanupFiles(certFileID, privateKeyFileID)
|
|
return nil, fmt.Errorf("failed to save CA to database: %w", err)
|
|
}
|
|
|
|
return &createdCA, nil
|
|
}
|
|
|
|
// GetCA retrieves a certificate authority by ID
|
|
func (s *CertificateAuthorityService) GetCA(id string) (*models.CertificateAuthority, error) {
|
|
ca, err := s.caRepo.GetByID(id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CA not found: %w", err)
|
|
}
|
|
return &ca, nil
|
|
}
|
|
|
|
// GetAllCAs retrieves all certificate authorities
|
|
func (s *CertificateAuthorityService) GetAllCAs() ([]models.CertificateAuthority, error) {
|
|
cas, err := s.caRepo.GetAll()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve CAs: %w", err)
|
|
}
|
|
return cas, nil
|
|
}
|
|
|
|
// GetRootCAs retrieves all root certificate authorities
|
|
func (s *CertificateAuthorityService) GetRootCAs() ([]models.CertificateAuthority, error) {
|
|
allCAs, err := s.caRepo.GetAll()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve CAs: %w", err)
|
|
}
|
|
|
|
var rootCAs []models.CertificateAuthority
|
|
for _, ca := range allCAs {
|
|
if ca.Root {
|
|
rootCAs = append(rootCAs, ca)
|
|
}
|
|
}
|
|
|
|
return rootCAs, nil
|
|
}
|
|
|
|
// GetIntermediateCAs retrieves all intermediate certificate authorities
|
|
func (s *CertificateAuthorityService) GetIntermediateCAs() ([]models.CertificateAuthority, error) {
|
|
allCAs, err := s.caRepo.GetAll()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve CAs: %w", err)
|
|
}
|
|
|
|
var intermediateCAs []models.CertificateAuthority
|
|
for _, ca := range allCAs {
|
|
if !ca.Root {
|
|
intermediateCAs = append(intermediateCAs, ca)
|
|
}
|
|
}
|
|
|
|
return intermediateCAs, nil
|
|
}
|
|
|
|
// GetCAByParent retrieves all certificate authorities under a specific parent
|
|
func (s *CertificateAuthorityService) GetCAByParent(parentID string) ([]models.CertificateAuthority, error) {
|
|
allCAs, err := s.caRepo.GetAll()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve CAs: %w", err)
|
|
}
|
|
|
|
var childCAs []models.CertificateAuthority
|
|
for _, ca := range allCAs {
|
|
if ca.ParentID != nil && ca.ParentID.String() == parentID {
|
|
childCAs = append(childCAs, ca)
|
|
}
|
|
}
|
|
|
|
return childCAs, nil
|
|
}
|
|
|
|
// GetCACertificate retrieves the certificate for a CA
|
|
func (s *CertificateAuthorityService) GetCACertificate(caID string) (*x509.Certificate, error) {
|
|
ca, err := s.caRepo.GetByID(caID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CA not found: %w", err)
|
|
}
|
|
|
|
cert, _, err := s.loadCACertificateAndKey(&ca)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load CA certificate: %w", err)
|
|
}
|
|
|
|
return cert, nil
|
|
}
|
|
|
|
// GetCAPrivateKey retrieves the private key for a CA
|
|
func (s *CertificateAuthorityService) GetCAPrivateKey(caID string) (interface{}, error) {
|
|
ca, err := s.caRepo.GetByID(caID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CA not found: %w", err)
|
|
}
|
|
|
|
_, privateKey, err := s.loadCACertificateAndKey(&ca)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load CA private key: %w", err)
|
|
}
|
|
|
|
return privateKey, nil
|
|
}
|
|
|
|
// UpdateCA updates a certificate authority
|
|
func (s *CertificateAuthorityService) UpdateCA(ca *models.CertificateAuthority) error {
|
|
return s.caRepo.Update(*ca)
|
|
}
|
|
|
|
// DeleteCA deletes a certificate authority
|
|
func (s *CertificateAuthorityService) DeleteCA(caID string) error {
|
|
ca, err := s.caRepo.GetByID(caID)
|
|
if err != nil {
|
|
return fmt.Errorf("CA not found: %w", err)
|
|
}
|
|
|
|
// Check if CA has children
|
|
children, err := s.GetCAByParent(caID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to check CA children: %w", err)
|
|
}
|
|
|
|
if len(children) > 0 {
|
|
return fmt.Errorf("cannot delete CA with children: %d intermediate CAs depend on this CA", len(children))
|
|
}
|
|
|
|
// Delete from database
|
|
if err := s.caRepo.Delete(caID); err != nil {
|
|
return fmt.Errorf("failed to delete CA from database: %w", err)
|
|
}
|
|
|
|
// Clean up files
|
|
s.cleanupFiles(ca.FileID, ca.PrivateKeyID)
|
|
|
|
return nil
|
|
}
|
|
|
|
// RevokeCA revokes a certificate authority (marks as revoked but keeps in database)
|
|
func (s *CertificateAuthorityService) RevokeCA(caID string, reason string) error {
|
|
ca, err := s.caRepo.GetByID(caID)
|
|
if err != nil {
|
|
return fmt.Errorf("CA not found: %w", err)
|
|
}
|
|
|
|
// Update CA with revocation information
|
|
ca.Description = fmt.Sprintf("%s [REVOKED: %s]", ca.Description, reason)
|
|
|
|
if err := s.caRepo.Update(ca); err != nil {
|
|
return fmt.Errorf("failed to revoke CA: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateCAChain validates the certificate chain for a CA
|
|
func (s *CertificateAuthorityService) ValidateCAChain(caID string) error {
|
|
ca, err := s.caRepo.GetByID(caID)
|
|
if err != nil {
|
|
return fmt.Errorf("CA not found: %w", err)
|
|
}
|
|
|
|
// Load CA certificate
|
|
cert, _, err := s.loadCACertificateAndKey(&ca)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load CA certificate: %w", err)
|
|
}
|
|
|
|
// If it's a root CA, validate it's self-signed
|
|
if ca.Root {
|
|
if err := cert.CheckSignatureFrom(cert); err != nil {
|
|
return fmt.Errorf("root CA certificate is not properly self-signed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// For intermediate CAs, validate the chain
|
|
if ca.ParentID == nil {
|
|
return fmt.Errorf("intermediate CA must have a parent")
|
|
}
|
|
|
|
parentCA, err := s.caRepo.GetByID(ca.ParentID.String())
|
|
if err != nil {
|
|
return fmt.Errorf("parent CA not found: %w", err)
|
|
}
|
|
|
|
parentCert, _, err := s.loadCACertificateAndKey(&parentCA)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load parent CA certificate: %w", err)
|
|
}
|
|
|
|
// Validate certificate chain
|
|
if err := cert.CheckSignatureFrom(parentCert); err != nil {
|
|
return fmt.Errorf("CA certificate is not properly signed by parent: %w", err)
|
|
}
|
|
|
|
// Check validity period
|
|
now := time.Now()
|
|
if now.Before(cert.NotBefore) {
|
|
return fmt.Errorf("CA certificate is not yet valid")
|
|
}
|
|
if now.After(cert.NotAfter) {
|
|
return fmt.Errorf("CA certificate has expired")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Helper methods
|
|
|
|
// saveCertificate saves a certificate to a file and returns the file ID
|
|
func (s *CertificateAuthorityService) saveCertificate(cert *x509.Certificate, prefix string) (string, error) {
|
|
// Create certificate directory if it doesn't exist
|
|
if err := os.MkdirAll(s.certDir, 0755); err != nil {
|
|
return "", fmt.Errorf("failed to create certificate directory: %w", err)
|
|
}
|
|
|
|
// Generate file ID
|
|
fileID := fmt.Sprintf("%s-%s", prefix, uuid.New().String())
|
|
|
|
// Export certificate to PEM
|
|
exporter := utils.NewCertificateExporter()
|
|
certPEM, err := exporter.ExportCertificateToPEM(cert)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to export certificate to PEM: %w", err)
|
|
}
|
|
|
|
// Save to file
|
|
certPath := filepath.Join(s.certDir, fmt.Sprintf("%s.crt", fileID))
|
|
if err := os.WriteFile(certPath, certPEM, 0644); err != nil {
|
|
return "", fmt.Errorf("failed to write certificate file: %w", err)
|
|
}
|
|
|
|
return fileID, nil
|
|
}
|
|
|
|
// savePrivateKey saves a private key to a file and returns the file ID
|
|
func (s *CertificateAuthorityService) savePrivateKey(privateKey interface{}, prefix string) (string, error) {
|
|
// Create private key directory if it doesn't exist
|
|
if err := os.MkdirAll(s.privateDir, 0700); err != nil {
|
|
return "", fmt.Errorf("failed to create private key directory: %w", err)
|
|
}
|
|
|
|
// Generate file ID
|
|
fileID := fmt.Sprintf("%s-%s", prefix, uuid.New().String())
|
|
|
|
// Export private key to PEM
|
|
exporter := utils.NewCertificateExporter()
|
|
keyPEM, err := exporter.ExportPrivateKeyToPEM(privateKey)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to export private key to PEM: %w", err)
|
|
}
|
|
|
|
// Save to file
|
|
keyPath := filepath.Join(s.privateDir, fmt.Sprintf("%s.key", fileID))
|
|
if err := os.WriteFile(keyPath, keyPEM, 0600); err != nil {
|
|
return "", fmt.Errorf("failed to write private key file: %w", err)
|
|
}
|
|
|
|
return fileID, nil
|
|
}
|
|
|
|
// loadCACertificateAndKey loads a CA certificate and private key from files
|
|
func (s *CertificateAuthorityService) loadCACertificateAndKey(ca *models.CertificateAuthority) (*x509.Certificate, interface{}, error) {
|
|
// Load certificate
|
|
certPath := filepath.Join(s.certDir, fmt.Sprintf("%s.crt", ca.FileID))
|
|
certPEM, err := os.ReadFile(certPath)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to read certificate file: %w", err)
|
|
}
|
|
|
|
block, _ := pem.Decode(certPEM)
|
|
if block == nil {
|
|
return nil, nil, fmt.Errorf("failed to decode certificate PEM")
|
|
}
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse certificate: %w", err)
|
|
}
|
|
|
|
// Load private key
|
|
keyPath := filepath.Join(s.privateDir, fmt.Sprintf("%s.key", ca.PrivateKeyID))
|
|
keyPEM, err := os.ReadFile(keyPath)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to read private key file: %w", err)
|
|
}
|
|
|
|
block, _ = pem.Decode(keyPEM)
|
|
if block == nil {
|
|
return nil, nil, fmt.Errorf("failed to decode private key PEM")
|
|
}
|
|
|
|
var privateKey interface{}
|
|
switch block.Type {
|
|
case "RSA PRIVATE KEY":
|
|
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
case "EC PRIVATE KEY":
|
|
privateKey, err = x509.ParseECPrivateKey(block.Bytes)
|
|
default:
|
|
return nil, nil, fmt.Errorf("unsupported private key type: %s", block.Type)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse private key: %w", err)
|
|
}
|
|
|
|
return cert, privateKey, nil
|
|
}
|
|
|
|
// cleanupFiles removes certificate and private key files
|
|
func (s *CertificateAuthorityService) cleanupFiles(certFileID, privateKeyFileID string) {
|
|
if certFileID != "" {
|
|
certPath := filepath.Join(s.certDir, fmt.Sprintf("%s.crt", certFileID))
|
|
os.Remove(certPath)
|
|
}
|
|
|
|
if privateKeyFileID != "" {
|
|
keyPath := filepath.Join(s.privateDir, fmt.Sprintf("%s.key", privateKeyFileID))
|
|
os.Remove(keyPath)
|
|
}
|
|
}
|
|
|
|
// validateRootCARequest validates a root CA creation request
|
|
func (s *CertificateAuthorityService) validateRootCARequest(req *CreateRootCARequest) error {
|
|
if req.Name == "" {
|
|
return fmt.Errorf("name is required")
|
|
}
|
|
if req.CommonName == "" {
|
|
return fmt.Errorf("common name is required")
|
|
}
|
|
if req.Organization == "" {
|
|
return fmt.Errorf("organization is required")
|
|
}
|
|
if req.Country == "" {
|
|
return fmt.Errorf("country is required")
|
|
}
|
|
if req.OrganizationID == uuid.Nil {
|
|
return fmt.Errorf("organization ID is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// validateIntermediateCARequest validates an intermediate CA creation request
|
|
func (s *CertificateAuthorityService) validateIntermediateCARequest(req *CreateIntermediateCARequest) error {
|
|
if req.Name == "" {
|
|
return fmt.Errorf("name is required")
|
|
}
|
|
if req.CommonName == "" {
|
|
return fmt.Errorf("common name is required")
|
|
}
|
|
if req.Organization == "" {
|
|
return fmt.Errorf("organization is required")
|
|
}
|
|
if req.Country == "" {
|
|
return fmt.Errorf("country is required")
|
|
}
|
|
if req.OrganizationID == uuid.Nil {
|
|
return fmt.Errorf("organization ID is required")
|
|
}
|
|
if req.ParentCAID == uuid.Nil {
|
|
return fmt.Errorf("parent CA ID is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Request structures
|
|
|
|
// CreateRootCARequest represents a request to create a root CA
|
|
type CreateRootCARequest struct {
|
|
Name string
|
|
Description string
|
|
CommonName string
|
|
Organization string
|
|
OrganizationalUnit string
|
|
Country string
|
|
State string
|
|
Locality string
|
|
Street string
|
|
Address string
|
|
PostalCode string
|
|
Email string
|
|
OrganizationID uuid.UUID
|
|
ValidityYears int // Custom validity period in years (0 = use default)
|
|
}
|
|
|
|
// CreateIntermediateCARequest represents a request to create an intermediate CA
|
|
type CreateIntermediateCARequest struct {
|
|
Name string
|
|
Description string
|
|
CommonName string
|
|
Organization string
|
|
OrganizationalUnit string
|
|
Country string
|
|
State string
|
|
Locality string
|
|
Street string
|
|
Address string
|
|
PostalCode string
|
|
Email string
|
|
OrganizationID uuid.UUID
|
|
ParentCAID uuid.UUID
|
|
ValidityYears int // Custom validity period in years (0 = use default)
|
|
}
|