Documentation ¶
Index ¶
Constants ¶
View Source
const ( KeyOutDir = "out.dir" KeyOutCert = "out.cert" KeyOutKey = "out.key" KeyOutCA = "out.ca" KeyCommonName = "commonName" KeyIsCA = "isCA" KeyDuration = "duration" KeyRenewBefore = "renewBefore" KeyKeyUsages = "keyUsages" KeyExtKeyUsages = "extKeyUsages" KeyDNSNames = "dnsNames" KeyIPAddresses = "ipAddresses" KeyCountries = "subject.countries" KeyOrganizations = "subject.organizations" KeyOrganizationalUnits = "subject.organizationalUnits" KeyLocalities = "subject.localities" KeyProvinces = "subject.provinces" KeyStreetAddresses = "subject.streetAddresses" KeyPostalCodes = "subject.postalCodes" KeyPrivateKeyAlgorithm = "privateKey.algorithm" KeyPrivateKeySize = "privateKey.size" KeyIssuerDir = "issuer.dir" KeyIssuerPublicKey = "issuer.publicKey" KeyIssuerPrivateKey = "issuer.privateKey" )
View Source
const ( MinRSAKeySize = 2048 MaxRSAKeySize = 8192 RSA = "rsa" ECDSA = "ecdsa" ED25519 = "ed25519" )
Variables ¶
View Source
var ( ErrOpenCertificateRequestFile = errors.New("open file") ErrReadCertificateRequestFile = errors.New("read file") ErrInvalidKeyUsages = errors.New("invalid key usages") ErrInvalidExtKeyUsages = errors.New("invalid ext key usages") ErrInvalidIPAddress = errors.New("invalid ip addresses") ErrMissingMandatoryField = errors.New("missing mandatory field") )
View Source
var ( ErrLoadIssuerKeyPair = errors.New("load issuer key pair") ErrParseIssuerCertificate = errors.New("parse issuer certificate") ErrCreateFile = errors.New("create file") ErrReadFile = errors.New("read file") ErrParseCertificate = errors.New("parse certificate") ErrEncode = errors.New("encode") ErrReadDir = errors.New("read directory") )
View Source
var ( ErrGenerateKey = errors.New("generate key") ErrGenerateSerialNumber = errors.New("generate serial number") ErrGenerateCert = errors.New("generate cert") ErrCopyCA = errors.New("copy CA") ErrRSAKeySizeTooWeak = fmt.Errorf("RSA key size too weak, minimum is %d", MinRSAKeySize) ErrRSAKeySizeTooBig = fmt.Errorf("RSA key size too big, maximum is %d", MaxRSAKeySize) ErrUnsupportedPrivateKeyAlgorithm = fmt.Errorf("unsupported private key algorithm") ErrEncodePrivateKey = fmt.Errorf("encode private key") ErrUnsupportedECDSAKeySize = errors.New("unsupported ecdsa key size") )
View Source
var CopyCA = func(issuer *Issuer, path string) error { pemCert := &pem.Block{Type: "CERTIFICATE", Bytes: issuer.PublicKey.Raw} err := WritePemToFile(pemCert, path) if err != nil { return fmt.Errorf(format.WrapErrors, ErrCopyCA, err) } return nil }
View Source
var (
ErrInvalidPEMBlock = errors.New("invalid PEM block")
)
View Source
var FileDoesNotExists = func(file string) bool { _, err := os.Stat(file) return errors.Is(err, os.ErrNotExist) }
View Source
var GenerateCertificate = func(req CertificateRequest, key crypto.PrivateKey, issuer *Issuer) error { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { return fmt.Errorf(format.WrapErrors, ErrGenerateSerialNumber, err) } keyUsage := x509.KeyUsageDigitalSignature if _, isRSA := key.(*rsa.PrivateKey); isRSA { keyUsage |= x509.KeyUsageKeyEncipherment } if req.IsCA { keyUsage |= x509.KeyUsageCertSign } notBefore := time.Now() template := &x509.Certificate{ Subject: pkix.Name{ CommonName: req.CommonName, Country: req.Countries, Organization: req.Organizations, OrganizationalUnit: req.OrganizationalUnits, Locality: req.Localities, Province: req.Provinces, StreetAddress: req.StreetAddresses, PostalCode: req.PostalCodes, }, SerialNumber: serialNumber, IsCA: req.IsCA, NotBefore: notBefore, NotAfter: notBefore.Add(req.Duration), KeyUsage: keyUsage, ExtKeyUsage: req.ExtKeyUsage, DNSNames: req.DNSNames, IPAddresses: req.IPAddresses, BasicConstraintsValid: true, } issuerCert := template signerKey := key if issuer != nil { issuerCert = issuer.PublicKey signerKey = issuer.PrivateKey } certBytes, err := x509.CreateCertificate(rand.Reader, template, issuerCert, publicKey(key), signerKey) if err != nil { return fmt.Errorf(format.WrapErrors, ErrGenerateCert, err) } pemCert := &pem.Block{Type: "CERTIFICATE", Bytes: certBytes} err = WritePemToFile(pemCert, req.OutCertPath) if err != nil { return fmt.Errorf(format.WrapErrors, ErrGenerateCert, err) } return nil }
View Source
var GenerateOutFilesFromRequest = func(req CertificateRequest, issuer *Issuer) { logrus.Infof("Generate key to %s", req.OutKeyPath) key, err := GeneratePrivateKey(req) if err != nil { logError(err) return } logrus.Infof("Generate certificate to %s", req.OutCertPath) if err := GenerateCertificate(req, key, issuer); err != nil { logError(err) return } if issuer != nil { logrus.Infof("Copy CA to %s", req.OutCAPath) if err := CopyCA(issuer, req.OutCAPath); err != nil { logError(err) return } } }
View Source
var GeneratePrivateKey = func(req CertificateRequest) (crypto.PrivateKey, error) { algorithm := req.PrivateKey.Algorithm if algorithm == "" { algorithm = RSA } var key crypto.PrivateKey var pemBlock *pem.Block var err error switch strings.ToLower(algorithm) { case RSA: key, pemBlock, err = generateRSAPrivateKey(req) case ECDSA: key, pemBlock, err = generateECPrivateKey(req) case ED25519: key, pemBlock, err = generateEd25519PrivateKey(req) default: return nil, fmt.Errorf(format.WrapErrorString, ErrUnsupportedPrivateKeyAlgorithm, algorithm) } if err != nil { return nil, fmt.Errorf(format.WrapErrors, ErrGenerateKey, err) } err = WritePemToFile(pemBlock, req.OutKeyPath) if err != nil { return nil, fmt.Errorf(format.WrapErrors, ErrGenerateKey, err) } return key, nil }
View Source
var HandleCertificateRequestFile = func(file string) { if _, err := config.GetExtension(file); err != nil { return } logrus.Infof("Handle certificate request %s", file) req, err := LoadCertificateRequest(file) if err != nil { logrus.Errorf("Failed to load certificate request: %v", err) return } issuer, err := LoadIssuer(req.IssuerPath) if err != nil { logrus.Errorf("Invalid issuer: %v", err) return } if FileDoesNotExists(req.OutCertPath) { if ok := MakeParentsDirectories(req.OutCertPath); !ok { return } GenerateOutFilesFromRequest(req, issuer) return } cert, err := LoadCertFromFile(req.OutCertPath) if err != nil { logrus.Errorf("Invalid certificate %s: %v", req.OutCertPath, err) GenerateOutFilesFromRequest(req, issuer) return } if cert.NotAfter.Before(time.Now().Add(req.RenewBefore)) { logrus.Infof("Expired certificate %s", req.OutCertPath) GenerateOutFilesFromRequest(req, issuer) return } }
View Source
var LoadCertFromFile = func(file string) (*x509.Certificate, error) { b, err := os.ReadFile(file) if err != nil { return nil, fmt.Errorf(format.WrapErrors, ErrReadFile, err) } certPEMBlock, _ := pem.Decode(b) if certPEMBlock == nil || certPEMBlock.Type != "CERTIFICATE" { return nil, ErrInvalidPEMBlock } x509Cert, err := x509.ParseCertificate(certPEMBlock.Bytes) if err != nil { return nil, fmt.Errorf(format.WrapErrors, ErrParseCertificate, err) } return x509Cert, nil }
View Source
var LoadCertificateRequest = func(path string) (CertificateRequest, error) { conf := viper.New() file, err := os.Open(path) if err != nil { return CertificateRequest{}, fmt.Errorf(format.WrapErrors, ErrOpenCertificateRequestFile, err) } ext, err := config.GetExtension(path) if err != nil { return CertificateRequest{}, err } conf.SetConfigType(ext) if err := conf.ReadConfig(file); err != nil { return CertificateRequest{}, fmt.Errorf(format.WrapErrors, ErrReadCertificateRequestFile, err) } conf.SetDefault(KeyOutCert, "tls.crt") conf.SetDefault(KeyOutKey, "tls.key") conf.SetDefault(KeyOutCA, "ca.crt") conf.SetDefault(KeyCountries, config.DefaultCountries) conf.SetDefault(KeyOrganizations, config.DefaultOrganizations) conf.SetDefault(KeyOrganizationalUnits, config.DefaultOrganizationalUnits) conf.SetDefault(KeyLocalities, config.DefaultLocalities) conf.SetDefault(KeyProvinces, config.DefaultProvinces) conf.SetDefault(KeyStreetAddresses, config.DefaultStreetAddresses) conf.SetDefault(KeyPostalCodes, config.DefaultPostalCodes) conf.SetDefault(KeyIssuerPublicKey, "ca.crt") conf.SetDefault(KeyIssuerPrivateKey, "ca.key") outDir := conf.GetString(KeyOutDir) if outDir == "" { return CertificateRequest{}, fmt.Errorf(format.WrapErrorString, ErrMissingMandatoryField, KeyOutDir) } issuerDir := conf.GetString(KeyIssuerDir) var issuerPath IssuerPath if issuerDir != "" { issuerPubKeyPath := filepath.Join(issuerDir, conf.GetString(KeyIssuerPublicKey)) issuerPrivKeyPath := filepath.Join(issuerDir, conf.GetString(KeyIssuerPrivateKey)) issuerPath = IssuerPath{PublicKey: issuerPubKeyPath, PrivateKey: issuerPrivKeyPath} } req := CertificateRequest{ OutCertPath: filepath.Join(outDir, conf.GetString(KeyOutCert)), OutKeyPath: filepath.Join(outDir, conf.GetString(KeyOutKey)), OutCAPath: filepath.Join(outDir, conf.GetString(KeyOutCA)), CommonName: conf.GetString(KeyCommonName), IsCA: conf.GetBool(KeyIsCA), Countries: conf.GetStringSlice(KeyCountries), Organizations: conf.GetStringSlice(KeyOrganizations), OrganizationalUnits: conf.GetStringSlice(KeyOrganizationalUnits), Localities: conf.GetStringSlice(KeyLocalities), Provinces: conf.GetStringSlice(KeyProvinces), StreetAddresses: conf.GetStringSlice(KeyStreetAddresses), PostalCodes: conf.GetStringSlice(KeyPostalCodes), Duration: conf.GetDuration(KeyDuration), RenewBefore: conf.GetDuration(KeyRenewBefore), PrivateKey: PrivateKey{Algorithm: conf.GetString(KeyPrivateKeyAlgorithm), Size: conf.GetInt(KeyPrivateKeySize)}, IssuerPath: issuerPath, } for _, s := range conf.GetStringSlice(KeyKeyUsages) { keyUsage, err := findKeyUsage(s) if err != nil { return CertificateRequest{}, fmt.Errorf(format.WrapErrorString, ErrInvalidKeyUsages, s) } req.KeyUsage |= keyUsage } for _, s := range conf.GetStringSlice(KeyExtKeyUsages) { extKeyUsage, err := findExtKeyUsage(s) if err != nil { return CertificateRequest{}, fmt.Errorf(format.WrapErrorString, ErrInvalidExtKeyUsages, s) } req.ExtKeyUsage = append(req.ExtKeyUsage, extKeyUsage) } for _, dnsName := range conf.GetStringSlice(KeyDNSNames) { req.DNSNames = append(req.DNSNames, dnsName) } for _, s := range conf.GetStringSlice(KeyIPAddresses) { ipAddr := net.ParseIP(s) if ipAddr == nil { return CertificateRequest{}, fmt.Errorf(format.WrapErrorString, ErrInvalidIPAddress, s) } req.IPAddresses = append(req.IPAddresses, ipAddr) } return req, nil }
View Source
var LoadCertificateRequests = func(dir string) { files, err := ReadDir(dir) if err != nil { logrus.Errorf("Failed to read directory %s: %v", dir, err) return } for _, file := range files { HandleCertificateRequestFile(file) } }
View Source
var LoadIssuer = func(path IssuerPath) (*Issuer, error) { if path.PublicKey == "" || path.PrivateKey == "" { return nil, nil } rootCA, err := tls.LoadX509KeyPair(path.PublicKey, path.PrivateKey) if err != nil { return nil, fmt.Errorf(format.WrapErrors, ErrLoadIssuerKeyPair, err) } caKey := rootCA.PrivateKey ca, err := x509.ParseCertificate(rootCA.Certificate[0]) if err != nil { return nil, fmt.Errorf(format.WrapErrors, ErrParseIssuerCertificate, err) } return &Issuer{PublicKey: ca, PrivateKey: caKey}, nil }
View Source
var MakeParentsDirectories = func(path string) bool { dir := filepath.Dir(path) if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { if err := os.MkdirAll(dir, 0755); err != nil { return false } } return true }
View Source
var ReadDir = func(dir string) ([]string, error) { entries, err := os.ReadDir(dir) if err != nil { return nil, fmt.Errorf(format.WrapErrors, ErrReadDir, err) } files := make([]string, 0, len(entries)) for _, entry := range entries { info, _ := entry.Info() if !info.IsDir() { files = append(files, filepath.Join(dir, info.Name())) } } return files, nil }
View Source
var WritePemToFile = func(b *pem.Block, file string) error { pemFile, err := os.Create(file) if err != nil { return fmt.Errorf(format.WrapErrors, ErrCreateFile, err) } defer func() { _ = pemFile.Close() }() err = pem.Encode(pemFile, b) if err != nil { return fmt.Errorf(format.WrapErrors, ErrEncode, err) } return nil }
Functions ¶
Types ¶
type CertificateRequest ¶
type CertificateRequest struct { OutCertPath string OutKeyPath string OutCAPath string CommonName string IsCA bool Countries []string Organizations []string OrganizationalUnits []string Localities []string Provinces []string StreetAddresses []string PostalCodes []string Duration time.Duration RenewBefore time.Duration KeyUsage x509.KeyUsage ExtKeyUsage []x509.ExtKeyUsage DNSNames []string IPAddresses []net.IP PrivateKey PrivateKey IssuerPath IssuerPath }
type Issuer ¶
type Issuer struct { PublicKey *x509.Certificate PrivateKey crypto.PrivateKey }
type IssuerPath ¶
type PrivateKey ¶
Click to show internal directories.
Click to hide internal directories.