github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/tlsx/cert.go (about) 1 package tlsx 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/rand" 6 "crypto/rsa" 7 "crypto/tls" 8 "crypto/x509" 9 "crypto/x509/pkix" 10 "encoding/base64" 11 "encoding/pem" 12 "fmt" 13 "math/big" 14 "os" 15 "time" 16 17 "github.com/pkg/errors" 18 ) 19 20 // ErrNoCertificatesConfigured is returned when no TLS configuration was found. 21 var ErrNoCertificatesConfigured = errors.New("no tls configuration was found") 22 23 // ErrInvalidCertificateConfiguration is returned when an invaloid TLS configuration was found. 24 var ErrInvalidCertificateConfiguration = errors.New("tls configuration is invalid") 25 26 // HTTPSCertificate returns loads a HTTP over TLS Certificate by looking at environment variables. 27 func HTTPSCertificate() ([]tls.Certificate, error) { 28 prefix := "HTTPS_TLS" 29 return Certificate( 30 os.Getenv(prefix+"_CERT"), os.Getenv(prefix+"_KEY"), 31 os.Getenv(prefix+"_CERT_PATH"), os.Getenv(prefix+"_KEY_PATH"), 32 ) 33 } 34 35 // HTTPSCertificateHelpMessage returns a help message for configuring HTTP over TLS Certificates. 36 func HTTPSCertificateHelpMessage() string { 37 return CertificateHelpMessage("HTTPS_TLS") 38 } 39 40 // CertificateHelpMessage returns a help message for configuring TLS Certificates. 41 func CertificateHelpMessage(prefix string) string { 42 return `- ` + prefix + `_CERT_PATH: The path to the TLS certificate (pem encoded). 43 Example: ` + prefix + `_CERT_PATH=~/cert.pem 44 45 - ` + prefix + `_KEY_PATH: The path to the TLS private key (pem encoded). 46 Example: ` + prefix + `_KEY_PATH=~/key.pem 47 48 - ` + prefix + `_CERT: Base64 encoded (without padding) string of the TLS certificate (PEM encoded) to be used for HTTP over TLS (HTTPS). 49 Example: ` + prefix + `_CERT="-----BEGIN CERTIFICATE-----\nMIIDZTCCAk2gAwIBAgIEV5xOtDANBgkqhkiG9w0BAQ0FADA0MTIwMAYDVQQDDClP..." 50 51 - ` + prefix + `_KEY: Base64 encoded (without padding) string of the private key (PEM encoded) to be used for HTTP over TLS (HTTPS). 52 Example: ` + prefix + `_KEY="-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDg..." 53 ` 54 } 55 56 // Certificate returns loads a TLS Certificate by looking at environment variables. 57 func Certificate( 58 certString, keyString string, 59 certPath, keyPath string, 60 ) ([]tls.Certificate, error) { 61 if certString == "" && keyString == "" && certPath == "" && keyPath == "" { 62 return nil, errors.WithStack(ErrNoCertificatesConfigured) 63 } else if certString != "" && keyString != "" { 64 tlsCertBytes, err := base64.StdEncoding.DecodeString(certString) 65 if err != nil { 66 return nil, fmt.Errorf("unable to base64 decode the TLS certificate: %v", err) 67 } 68 tlsKeyBytes, err := base64.StdEncoding.DecodeString(keyString) 69 if err != nil { 70 return nil, fmt.Errorf("unable to base64 decode the TLS private key: %v", err) 71 } 72 73 cert, err := tls.X509KeyPair(tlsCertBytes, tlsKeyBytes) 74 if err != nil { 75 return nil, fmt.Errorf("unable to load X509 key pair: %v", err) 76 } 77 return []tls.Certificate{cert}, nil 78 } 79 80 if certPath != "" && keyPath != "" { 81 cert, err := tls.LoadX509KeyPair(certPath, keyPath) 82 if err != nil { 83 return nil, fmt.Errorf("unable to load X509 key pair from files: %v", err) 84 } 85 return []tls.Certificate{cert}, nil 86 } 87 88 return nil, errors.WithStack(ErrInvalidCertificateConfiguration) 89 } 90 91 // PublicKey returns the public key for a given key or nul. 92 func PublicKey(key interface{}) interface{} { 93 switch k := key.(type) { 94 case *rsa.PrivateKey: 95 return &k.PublicKey 96 case *ecdsa.PrivateKey: 97 return &k.PublicKey 98 default: 99 return nil 100 } 101 } 102 103 // CreateSelfSignedTLSCertificate creates a self-signed TLS certificate. 104 func CreateSelfSignedTLSCertificate(key interface{}) (*tls.Certificate, error) { 105 c, err := CreateSelfSignedCertificate(key) 106 if err != nil { 107 return nil, err 108 } 109 110 block, err := PEMBlockForKey(key) 111 if err != nil { 112 return nil, err 113 } 114 115 pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.Raw}) 116 pemKey := pem.EncodeToMemory(block) 117 cert, err := tls.X509KeyPair(pemCert, pemKey) 118 if err != nil { 119 return nil, err 120 } 121 122 return &cert, nil 123 } 124 125 // CreateSelfSignedCertificate creates a self-signed x509 certificate. 126 func CreateSelfSignedCertificate(key interface{}) (cert *x509.Certificate, err error) { 127 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 128 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 129 if err != nil { 130 return cert, errors.Errorf("failed to generate serial number: %s", err) 131 } 132 133 certificate := &x509.Certificate{ 134 SerialNumber: serialNumber, 135 Subject: pkix.Name{ 136 Organization: []string{"ORY GmbH"}, 137 CommonName: "ORY", 138 }, 139 Issuer: pkix.Name{ 140 Organization: []string{"ORY GmbH"}, 141 CommonName: "ORY", 142 }, 143 NotBefore: time.Now().UTC(), 144 NotAfter: time.Now().UTC().Add(time.Hour * 24 * 31), 145 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 146 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 147 BasicConstraintsValid: true, 148 } 149 150 certificate.IsCA = true 151 certificate.KeyUsage |= x509.KeyUsageCertSign 152 certificate.DNSNames = append(certificate.DNSNames, "localhost") 153 der, err := x509.CreateCertificate(rand.Reader, certificate, certificate, PublicKey(key), key) 154 if err != nil { 155 return cert, errors.Errorf("failed to create certificate: %s", err) 156 } 157 158 cert, err = x509.ParseCertificate(der) 159 if err != nil { 160 return cert, errors.Errorf("failed to encode private key: %s", err) 161 } 162 return cert, nil 163 } 164 165 // PEMBlockForKey returns a PEM-encoded block for key. 166 func PEMBlockForKey(key interface{}) (*pem.Block, error) { 167 switch k := key.(type) { 168 case *rsa.PrivateKey: 169 return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}, nil 170 case *ecdsa.PrivateKey: 171 b, err := x509.MarshalECPrivateKey(k) 172 if err != nil { 173 return nil, errors.WithStack(err) 174 } 175 return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil 176 default: 177 return nil, errors.New("Invalid key type") 178 } 179 }