123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- package tlsconfig
- import (
- "crypto/tls"
- "crypto/x509"
- "encoding/pem"
- "fmt"
- "io/ioutil"
- "os"
- "github.com/pkg/errors"
- )
- type Options struct {
- CAFile string
-
-
-
- CertFile string
- KeyFile string
-
- InsecureSkipVerify bool
-
- ClientAuth tls.ClientAuthType
-
-
-
- ExclusiveRootPools bool
- MinVersion uint16
-
-
- Passphrase string
- }
- var acceptedCBCCiphers = []uint16{
- tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
- tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
- tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
- tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
- tls.TLS_RSA_WITH_AES_256_CBC_SHA,
- tls.TLS_RSA_WITH_AES_128_CBC_SHA,
- }
- var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...)
- var allTLSVersions = map[uint16]struct{}{
- tls.VersionSSL30: {},
- tls.VersionTLS10: {},
- tls.VersionTLS11: {},
- tls.VersionTLS12: {},
- }
- func ServerDefault(ops ...func(*tls.Config)) *tls.Config {
- tlsconfig := &tls.Config{
-
- MinVersion: tls.VersionTLS10,
- PreferServerCipherSuites: true,
- CipherSuites: DefaultServerAcceptedCiphers,
- }
- for _, op := range ops {
- op(tlsconfig)
- }
- return tlsconfig
- }
- func ClientDefault(ops ...func(*tls.Config)) *tls.Config {
- tlsconfig := &tls.Config{
-
- MinVersion: tls.VersionTLS12,
- CipherSuites: clientCipherSuites,
- }
- for _, op := range ops {
- op(tlsconfig)
- }
- return tlsconfig
- }
- func certPool(caFile string, exclusivePool bool) (*x509.CertPool, error) {
-
- var (
- certPool *x509.CertPool
- err error
- )
- if exclusivePool {
- certPool = x509.NewCertPool()
- } else {
- certPool, err = SystemCertPool()
- if err != nil {
- return nil, fmt.Errorf("failed to read system certificates: %v", err)
- }
- }
- pem, err := ioutil.ReadFile(caFile)
- if err != nil {
- return nil, fmt.Errorf("could not read CA certificate %q: %v", caFile, err)
- }
- if !certPool.AppendCertsFromPEM(pem) {
- return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile)
- }
- return certPool, nil
- }
- func isValidMinVersion(version uint16) bool {
- _, ok := allTLSVersions[version]
- return ok
- }
- func adjustMinVersion(options Options, config *tls.Config) error {
- if options.MinVersion > 0 {
- if !isValidMinVersion(options.MinVersion) {
- return fmt.Errorf("Invalid minimum TLS version: %x", options.MinVersion)
- }
- if options.MinVersion < config.MinVersion {
- return fmt.Errorf("Requested minimum TLS version is too low. Should be at-least: %x", config.MinVersion)
- }
- config.MinVersion = options.MinVersion
- }
- return nil
- }
- func IsErrEncryptedKey(err error) bool {
- return errors.Cause(err) == x509.IncorrectPasswordError
- }
- func getPrivateKey(keyBytes []byte, passphrase string) ([]byte, error) {
-
- pemBlock, _ := pem.Decode(keyBytes)
- if pemBlock == nil {
- return nil, fmt.Errorf("no valid private key found")
- }
- var err error
- if x509.IsEncryptedPEMBlock(pemBlock) {
- keyBytes, err = x509.DecryptPEMBlock(pemBlock, []byte(passphrase))
- if err != nil {
- return nil, errors.Wrap(err, "private key is encrypted, but could not decrypt it")
- }
- keyBytes = pem.EncodeToMemory(&pem.Block{Type: pemBlock.Type, Bytes: keyBytes})
- }
- return keyBytes, nil
- }
- func getCert(options Options) ([]tls.Certificate, error) {
- if options.CertFile == "" && options.KeyFile == "" {
- return nil, nil
- }
- errMessage := "Could not load X509 key pair"
- cert, err := ioutil.ReadFile(options.CertFile)
- if err != nil {
- return nil, errors.Wrap(err, errMessage)
- }
- prKeyBytes, err := ioutil.ReadFile(options.KeyFile)
- if err != nil {
- return nil, errors.Wrap(err, errMessage)
- }
- prKeyBytes, err = getPrivateKey(prKeyBytes, options.Passphrase)
- if err != nil {
- return nil, errors.Wrap(err, errMessage)
- }
- tlsCert, err := tls.X509KeyPair(cert, prKeyBytes)
- if err != nil {
- return nil, errors.Wrap(err, errMessage)
- }
- return []tls.Certificate{tlsCert}, nil
- }
- func Client(options Options) (*tls.Config, error) {
- tlsConfig := ClientDefault()
- tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
- if !options.InsecureSkipVerify && options.CAFile != "" {
- CAs, err := certPool(options.CAFile, options.ExclusiveRootPools)
- if err != nil {
- return nil, err
- }
- tlsConfig.RootCAs = CAs
- }
- tlsCerts, err := getCert(options)
- if err != nil {
- return nil, err
- }
- tlsConfig.Certificates = tlsCerts
- if err := adjustMinVersion(options, tlsConfig); err != nil {
- return nil, err
- }
- return tlsConfig, nil
- }
- func Server(options Options) (*tls.Config, error) {
- tlsConfig := ServerDefault()
- tlsConfig.ClientAuth = options.ClientAuth
- tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
- if err != nil {
- if os.IsNotExist(err) {
- return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.CertFile, options.KeyFile, err)
- }
- return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err)
- }
- tlsConfig.Certificates = []tls.Certificate{tlsCert}
- if options.ClientAuth >= tls.VerifyClientCertIfGiven && options.CAFile != "" {
- CAs, err := certPool(options.CAFile, options.ExclusiveRootPools)
- if err != nil {
- return nil, err
- }
- tlsConfig.ClientCAs = CAs
- }
- if err := adjustMinVersion(options, tlsConfig); err != nil {
- return nil, err
- }
- return tlsConfig, nil
- }
|