go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/certutil/key_pair.go (about)

     1  /*
     2  
     3  Copyright (c) 2023 - Present. Will Charczuk. All rights reserved.
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository.
     5  
     6  */
     7  
     8  package certutil
     9  
    10  import (
    11  	"crypto/tls"
    12  	"crypto/x509"
    13  	"encoding/base64"
    14  	"os"
    15  
    16  	"go.charczuk.com/sdk/errutil"
    17  )
    18  
    19  // NewKeyPairFromPaths returns a key pair from paths.
    20  func NewKeyPairFromPaths(certPath, keyPath string) KeyPair {
    21  	return KeyPair{CertPath: certPath, KeyPath: keyPath}
    22  }
    23  
    24  // KeyPair is an x509 pem key pair as strings.
    25  type KeyPair struct {
    26  	Cert       string `json:"cert,omitempty" yaml:"cert,omitempty"`
    27  	CertBase64 string `json:"certBase64,omitempty" yaml:"certBase64,omitempty"`
    28  	CertPath   string `json:"certPath,omitempty" yaml:"certPath,omitempty"`
    29  	Key        string `json:"key,omitempty" yaml:"key,omitempty"`
    30  	KeyBase64  string `json:"keyBase64,omitempty" yaml:"keyBase64,omitempty"`
    31  	KeyPath    string `json:"keyPath,omitempty" yaml:"keyPath,omitempty"`
    32  }
    33  
    34  // IsZero returns if the key pair is set or not.
    35  func (kp KeyPair) IsZero() bool {
    36  	return kp.Cert == "" &&
    37  		kp.Key == "" &&
    38  		kp.CertPath == "" &&
    39  		kp.KeyPath == ""
    40  }
    41  
    42  // IsCertPath returns if the keypair cert is a path.
    43  func (kp KeyPair) IsCertPath() bool {
    44  	return kp.Cert == "" && kp.CertPath != ""
    45  }
    46  
    47  // IsKeyPath returns if the keypair key is a path.
    48  func (kp KeyPair) IsKeyPath() bool {
    49  	return kp.Key == "" && kp.KeyPath != ""
    50  }
    51  
    52  // CertBytes returns the key pair cert bytes.
    53  func (kp KeyPair) CertBytes() ([]byte, error) {
    54  	if kp.Cert != "" {
    55  		return []byte(kp.Cert), nil
    56  	}
    57  	if kp.CertBase64 != "" {
    58  		return base64.StdEncoding.DecodeString(kp.CertBase64)
    59  	}
    60  	if kp.CertPath == "" {
    61  		return nil, errutil.New("error loading cert; no sources for cert data set")
    62  	}
    63  	contents, err := os.ReadFile(os.ExpandEnv(kp.CertPath))
    64  	if err != nil {
    65  		return nil, errutil.New("error loading cert from path", errutil.OptInner(err), errutil.OptMessage(kp.CertPath))
    66  	}
    67  	return contents, nil
    68  }
    69  
    70  // KeyBytes returns the key pair key bytes.
    71  func (kp KeyPair) KeyBytes() ([]byte, error) {
    72  	if kp.Key != "" {
    73  		return []byte(kp.Key), nil
    74  	}
    75  	if kp.KeyBase64 != "" {
    76  		return base64.StdEncoding.DecodeString(kp.KeyBase64)
    77  	}
    78  	if kp.KeyPath == "" {
    79  		return nil, errutil.New("error loading key; no sources for key data set")
    80  	}
    81  	contents, err := os.ReadFile(os.ExpandEnv(kp.KeyPath))
    82  	if err != nil {
    83  		return nil, errutil.New("error loading key from path", errutil.OptInner(err), errutil.OptMessage(kp.KeyPath))
    84  	}
    85  	return contents, nil
    86  }
    87  
    88  // String returns a string representation of the key pair.
    89  func (kp KeyPair) String() (output string) {
    90  	output = "[ "
    91  	if kp.Cert != "" {
    92  		output += "cert: <literal>"
    93  	} else if kp.CertPath != "" {
    94  		output += ("cert: " + os.ExpandEnv(kp.CertPath))
    95  	}
    96  	if kp.Key != "" {
    97  		output += ", key: <literal>"
    98  	} else if kp.KeyPath != "" {
    99  		output += (", key: " + os.ExpandEnv(kp.KeyPath))
   100  	}
   101  	output += " ]"
   102  	return output
   103  }
   104  
   105  // TLSCertificate returns the KeyPair as a tls.Certificate.
   106  func (kp KeyPair) TLSCertificate() (cert tls.Certificate, err error) {
   107  	certBytes, err := kp.CertBytes()
   108  	if err != nil {
   109  		err = errutil.New(err)
   110  		return
   111  	}
   112  	keyBytes, err := kp.KeyBytes()
   113  	if err != nil {
   114  		err = errutil.New(err)
   115  		return
   116  	}
   117  	cert, err = tls.X509KeyPair(certBytes, keyBytes)
   118  	if err != nil {
   119  		err = errutil.New(err)
   120  		return
   121  	}
   122  	return
   123  }
   124  
   125  // TLSCertificateWithLeaf returns the KeyPair as a tls.Certificate.
   126  func (kp KeyPair) TLSCertificateWithLeaf() (cert tls.Certificate, err error) {
   127  	cert, err = kp.TLSCertificate()
   128  	if err != nil {
   129  		err = errutil.New(err)
   130  		return
   131  	}
   132  	if len(cert.Certificate) == 0 {
   133  		err = errutil.New("invalid certificate; empty certificate list")
   134  		return
   135  	}
   136  	if cert.Leaf == nil {
   137  		cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
   138  		if err != nil {
   139  			err = errutil.New(err)
   140  			return
   141  		}
   142  	}
   143  	return
   144  }
   145  
   146  func (kp KeyPair) TLSConfig() (*tls.Config, error) {
   147  	cert, err := kp.TLSCertificate()
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	config := new(tls.Config)
   152  	config.Certificates = []tls.Certificate{cert}
   153  	return config, nil
   154  }