/* Copyright 2021-2022 CAcert Inc. SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package signing import ( "crypto" "crypto/rand" "crypto/x509" "encoding/asn1" "fmt" "time" "github.com/sirupsen/logrus" "git.cacert.org/cacert-gosigner/internal/x509/helper" ) // PolicyIdentifierCAcertClass3Policy is the ASN.1 object identifier for the CAcert Class3 policy from [OIDAllocation] // // [OIDAllocation]: https://wiki.cacert.org/OidAllocation var PolicyIdentifierCAcertClass3Policy = asn1.ObjectIdentifier{1, 3, 6, 4, 1, 18506, 2, 3} type X509Signing struct { signer Signer repo Repository } func NewX509Signing(signer Signer, repo Repository) *X509Signing { return &X509Signing{signer: signer, repo: repo} } type CertificateSigned struct { certificate *x509.Certificate } func (c CertificateSigned) Certificate() *x509.Certificate { return c.certificate } func (x *X509Signing) Sign(signingRequest *SignerRequest) (*SignerResponse, error) { certificateFromSigner, err := x.signer.SignCertificate(signingRequest) if err != nil { return nil, fmt.Errorf("could not sign certificate: %w", err) } err = x.repo.StoreCertificate(certificateFromSigner.Certificate) if err != nil { return nil, fmt.Errorf("could not store certificate: %w", err) } return certificateFromSigner, nil } type Handler interface { GetSigner(id string, profile string) (*X509Signing, error) } type ProfileUsage uint8 const ( UsageInvalid ProfileUsage = iota UsageOCSP UsageClient UsageCode UsagePerson UsageServer UsageServerClient UsageOrgClient UsageOrgCode UsageOrgEmail UsageOrgPerson UsageOrgServer UsageOrgServerClient ) var profileUsageNames = map[ProfileUsage]string{ UsageInvalid: "invalid", UsageOCSP: "ocsp", UsageClient: "client", UsageCode: "code", UsagePerson: "person", UsageServer: "server", UsageServerClient: "server_client", UsageOrgClient: "org_client", UsageOrgCode: "org_code", UsageOrgEmail: "org_email", UsageOrgPerson: "org_person", UsageOrgServer: "org_server", UsageOrgServerClient: "org_server_client", } func (p ProfileUsage) String() string { name, ok := profileUsageNames[p] if !ok { return fmt.Sprintf("unknown profile usage %d", p) } return name } var profileUsageDescriptions = map[ProfileUsage]string{ UsageInvalid: "Invalid certificate profile, not to be used", UsageOCSP: "OCSP responder signing certificate", UsageClient: "machine TLS client certificate", UsageCode: "individual code signing certificate", UsagePerson: "person identity certificate", UsageServer: "TLS server certificate", UsageServerClient: "combined TLS server and client certificate", UsageOrgClient: "organization machine TLS client certificate", UsageOrgCode: "organization code signing certificate", UsageOrgEmail: "organization email certificate", UsageOrgPerson: "organizational person identity certificate", UsageOrgServer: "organization TLS server certificate", UsageOrgServerClient: "combined organization TLS server and client certificate", } func (p ProfileUsage) Description() string { description, ok := profileUsageDescriptions[p] if !ok { return fmt.Sprintf("unknown profile usage %d", p) } return description } type Profile struct { logger *logrus.Logger privateKey crypto.Signer certificate *x509.Certificate ocspURLs []string name string usage ProfileUsage years int months int days int } func (p *Profile) SignCertificate(request *SignerRequest) (*SignerResponse, error) { signatureAlgorithm, err := p.determineSignatureAlgorithm(request.PreferredHash) if err != nil { return nil, err } notBefore := time.Now().UTC() serialNumber, err := helper.GenerateRandomSerial() if err != nil { return nil, fmt.Errorf("could not generate certificate serial number: %w", err) } const x509v3 = 3 template := &x509.Certificate{ SignatureAlgorithm: signatureAlgorithm, Version: x509v3, SerialNumber: serialNumber, Issuer: p.certificate.Subject, Subject: request.SubjectDN, NotBefore: notBefore, NotAfter: p.setNotAfter(notBefore), KeyUsage: p.keyUsage(), ExtKeyUsage: p.extKeyUsage(), IsCA: false, OCSPServer: p.ocspURLs, DNSNames: request.DNSNames, EmailAddresses: request.Emails, CRLDistributionPoints: p.certificate.CRLDistributionPoints, PolicyIdentifiers: p.policyIdentifiers(), } certBytes, err := x509.CreateCertificate(rand.Reader, template, p.certificate, request.CSR.PublicKey, p.privateKey) if err != nil { return nil, fmt.Errorf("certificate signing failed: %w", err) } signedCertificate, err := x509.ParseCertificate(certBytes) if err != nil { return nil, fmt.Errorf("failed to build certificate from DER data: %w", err) } return &SignerResponse{Certificate: signedCertificate}, nil } func (p *Profile) setNotAfter(before time.Time) time.Time { return before.AddDate(p.years, p.months, p.days) } func (p *Profile) determineSignatureAlgorithm(hash crypto.Hash) (x509.SignatureAlgorithm, error) { switch p.certificate.PublicKeyAlgorithm { case x509.RSA: switch hash { case crypto.SHA512: return x509.SHA512WithRSA, nil case crypto.SHA384: return x509.SHA384WithRSA, nil default: return x509.SHA256WithRSA, nil } case x509.ECDSA: switch hash { case crypto.SHA512: return x509.ECDSAWithSHA512, nil case crypto.SHA384: return x509.ECDSAWithSHA384, nil default: return x509.ECDSAWithSHA384, nil } default: return x509.UnknownSignatureAlgorithm, fmt.Errorf( "could not determine signature algorithm for public key algorithm %s", p.certificate.PublicKeyAlgorithm, ) } } func (p *Profile) keyUsage() x509.KeyUsage { switch p.usage { case UsageClient, UsagePerson, UsageServer, UsageServerClient, UsageOrgClient, UsageOrgEmail, UsageOrgPerson, UsageOrgServer, UsageOrgServerClient: return x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment default: return x509.KeyUsageDigitalSignature } } func (p *Profile) extKeyUsage() []x509.ExtKeyUsage { switch p.usage { case UsageClient, UsageOrgClient: return []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth} case UsagePerson, UsageOrgPerson: return []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageEmailProtection} case UsageOrgEmail: return []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection} case UsageCode, UsageOrgCode: return []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning} case UsageServer, UsageOrgServer: return []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} case UsageServerClient, UsageOrgServerClient: return []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth} case UsageOCSP: return []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning} default: return nil } } func (p *Profile) policyIdentifiers() []asn1.ObjectIdentifier { switch p.usage { case UsageOCSP: return nil default: return []asn1.ObjectIdentifier{PolicyIdentifierCAcertClass3Policy} } } func NewProfile( logger *logrus.Logger, privateKey crypto.Signer, certificate *x509.Certificate, name string, ocspURLs []string, useFor ProfileUsage, years int, months int, days int, ) *Profile { return &Profile{ privateKey: privateKey, certificate: certificate, ocspURLs: ocspURLs, name: name, usage: useFor, years: years, months: months, days: days, logger: logger, } } type SignCertificateHandler struct { profiles map[string]map[string]*X509Signing } func (s *SignCertificateHandler) GetSigner(issuerID string, profileName string) (*X509Signing, error) { profiles, ok := s.profiles[issuerID] if !ok { return nil, fmt.Errorf("no CA definition found for issuer id %s", issuerID) } profile, ok := profiles[profileName] if !ok { return nil, fmt.Errorf("unknown profile %s for issuer %s", profileName, issuerID) } return profile, nil } func NewSignCertificateHandler(profiles map[string]map[string]*X509Signing) *SignCertificateHandler { return &SignCertificateHandler{profiles: profiles} }