...

Source file src/github.com/ory/x/tlsx/cert.go

Documentation: github.com/ory/x/tlsx

     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  }
   180  

View as plain text