mirror of
https://github.com/traefik/traefik.git
synced 2026-04-16 19:21:36 +02:00
173 lines
5.3 KiB
Go
173 lines
5.3 KiB
Go
package api
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/rsa"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
certStatusEnabled = "enabled"
|
|
certStatusWarning = "warning"
|
|
certStatusExpired = "expired"
|
|
)
|
|
|
|
// certificateRepresentation represents a certificate in the API.
|
|
type certificateRepresentation struct {
|
|
Name string `json:"name"` // SHA-256 fingerprint of the DER-encoded certificate.
|
|
SANs []string `json:"sans"`
|
|
NotAfter time.Time `json:"notAfter"`
|
|
NotBefore time.Time `json:"notBefore"`
|
|
SerialNumber string `json:"serialNumber"`
|
|
CommonName string `json:"commonName"`
|
|
IssuerOrg string `json:"issuerOrg,omitempty"`
|
|
IssuerCN string `json:"issuerCN,omitempty"`
|
|
IssuerCountry string `json:"issuerCountry,omitempty"`
|
|
Organization string `json:"organization,omitempty"`
|
|
Country string `json:"country,omitempty"`
|
|
Version string `json:"version"`
|
|
KeyType string `json:"keyType"`
|
|
KeySize int `json:"keySize,omitempty"`
|
|
SignatureAlgorithm string `json:"signatureAlgorithm"`
|
|
CertFingerprint string `json:"certFingerprint"`
|
|
PublicKeyFingerprint string `json:"publicKeyFingerprint"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
// Interface methods for sort.go compatibility.
|
|
func (c certificateRepresentation) name() string {
|
|
return c.CommonName
|
|
}
|
|
|
|
func (c certificateRepresentation) status() string {
|
|
return c.Status
|
|
}
|
|
|
|
func (c certificateRepresentation) issuer() string {
|
|
if c.IssuerOrg != "" {
|
|
return c.IssuerOrg
|
|
}
|
|
return c.IssuerCN
|
|
}
|
|
|
|
func (c certificateRepresentation) validUntil() time.Time {
|
|
return c.NotAfter
|
|
}
|
|
|
|
// buildCertificateRepresentation builds a certificateRepresentation from an x509 certificate.
|
|
func buildCertificateRepresentation(cert *x509.Certificate) certificateRepresentation {
|
|
keyType, keySize := extractKeyInfo(cert)
|
|
certFingerprint, pubKeyFingerprint := extractFingerprints(cert)
|
|
issuerOrg, issuerCN, issuerCountry := extractIssuerInfo(cert)
|
|
organization, country := extractSubjectInfo(cert)
|
|
|
|
return certificateRepresentation{
|
|
Name: certFingerprint,
|
|
SANs: extractSANs(cert),
|
|
NotAfter: cert.NotAfter,
|
|
NotBefore: cert.NotBefore,
|
|
SerialNumber: cert.SerialNumber.String(),
|
|
CommonName: cert.Subject.CommonName,
|
|
IssuerOrg: issuerOrg,
|
|
IssuerCN: issuerCN,
|
|
IssuerCountry: issuerCountry,
|
|
Organization: organization,
|
|
Country: country,
|
|
Version: formatVersion(cert.Version),
|
|
KeyType: keyType,
|
|
KeySize: keySize,
|
|
SignatureAlgorithm: cert.SignatureAlgorithm.String(),
|
|
CertFingerprint: certFingerprint,
|
|
PublicKeyFingerprint: pubKeyFingerprint,
|
|
Status: getCertificateStatus(cert.NotAfter),
|
|
}
|
|
}
|
|
|
|
// extractSANs extracts Subject Alternative Names from a certificate.
|
|
func extractSANs(cert *x509.Certificate) []string {
|
|
sans := make([]string, 0, len(cert.DNSNames)+len(cert.IPAddresses))
|
|
sans = append(sans, cert.DNSNames...)
|
|
for _, ip := range cert.IPAddresses {
|
|
sans = append(sans, ip.String())
|
|
}
|
|
sort.Strings(sans)
|
|
return sans
|
|
}
|
|
|
|
// extractKeyInfo determines the key type and size from a certificate.
|
|
func extractKeyInfo(cert *x509.Certificate) (keyType string, keySize int) {
|
|
keyType = "Unknown"
|
|
keySize = 0
|
|
|
|
switch pubKey := cert.PublicKey.(type) {
|
|
case *rsa.PublicKey:
|
|
keyType = "RSA"
|
|
keySize = pubKey.N.BitLen()
|
|
case *ecdsa.PublicKey:
|
|
keyType = "ECDSA"
|
|
keySize = pubKey.Curve.Params().BitSize
|
|
}
|
|
|
|
return keyType, keySize
|
|
}
|
|
|
|
// extractFingerprints calculates SHA-256 fingerprints for certificate and public key.
|
|
func extractFingerprints(cert *x509.Certificate) (certFingerprint, pubKeyFingerprint string) {
|
|
certHash := sha256.Sum256(cert.Raw)
|
|
certFingerprint = hex.EncodeToString(certHash[:])
|
|
|
|
pubKeyBytes, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
|
|
if err == nil {
|
|
pubKeyHash := sha256.Sum256(pubKeyBytes)
|
|
pubKeyFingerprint = hex.EncodeToString(pubKeyHash[:])
|
|
}
|
|
|
|
return certFingerprint, pubKeyFingerprint
|
|
}
|
|
|
|
// extractIssuerInfo extracts issuer information from a certificate.
|
|
func extractIssuerInfo(cert *x509.Certificate) (org, cn, country string) {
|
|
if len(cert.Issuer.Organization) > 0 {
|
|
org = cert.Issuer.Organization[0]
|
|
}
|
|
cn = cert.Issuer.CommonName
|
|
if len(cert.Issuer.Country) > 0 {
|
|
country = cert.Issuer.Country[0]
|
|
}
|
|
return org, cn, country
|
|
}
|
|
|
|
// extractSubjectInfo extracts subject information from a certificate.
|
|
func extractSubjectInfo(cert *x509.Certificate) (organization, country string) {
|
|
if len(cert.Subject.Organization) > 0 {
|
|
organization = cert.Subject.Organization[0]
|
|
}
|
|
if len(cert.Subject.Country) > 0 {
|
|
country = cert.Subject.Country[0]
|
|
}
|
|
return organization, country
|
|
}
|
|
|
|
// formatVersion formats the X.509 version for display.
|
|
func formatVersion(version int) string {
|
|
return fmt.Sprintf("v%d", version)
|
|
}
|
|
|
|
// getCertificateStatus returns the status of a certificate based on its expiry.
|
|
func getCertificateStatus(notAfter time.Time) string {
|
|
remaining := time.Until(notAfter)
|
|
if remaining < 0 {
|
|
return certStatusExpired
|
|
}
|
|
// Show warning for certificates with validity less than 30 days left.
|
|
if remaining < 30*24*time.Hour {
|
|
return certStatusWarning
|
|
}
|
|
return certStatusEnabled
|
|
}
|