certificate_retriever.go raw

   1  // Copyright (c) 2016, 2018, 2025, Oracle and/or its affiliates.  All rights reserved.
   2  // This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
   3  
   4  package auth
   5  
   6  import (
   7  	"bytes"
   8  	"crypto/rsa"
   9  	"crypto/x509"
  10  	"encoding/pem"
  11  	"fmt"
  12  	"sync"
  13  
  14  	"github.com/nrdcg/oci-go-sdk/common/v1065"
  15  )
  16  
  17  // x509CertificateRetriever provides an X509 certificate with the RSA private key
  18  type x509CertificateRetriever interface {
  19  	Refresh() error
  20  	CertificatePemRaw() []byte
  21  	Certificate() *x509.Certificate
  22  	PrivateKeyPemRaw() []byte
  23  	PrivateKey() *rsa.PrivateKey
  24  }
  25  
  26  // urlBasedX509CertificateRetriever retrieves PEM-encoded X509 certificates from the given URLs.
  27  type urlBasedX509CertificateRetriever struct {
  28  	certURL           string
  29  	privateKeyURL     string
  30  	passphrase        string
  31  	certificatePemRaw []byte
  32  	certificate       *x509.Certificate
  33  	privateKeyPemRaw  []byte
  34  	privateKey        *rsa.PrivateKey
  35  	mux               sync.Mutex
  36  	dispatcher        common.HTTPRequestDispatcher
  37  }
  38  
  39  func newURLBasedX509CertificateRetriever(dispatcher common.HTTPRequestDispatcher, certURL, privateKeyURL, passphrase string) x509CertificateRetriever {
  40  	return &urlBasedX509CertificateRetriever{
  41  		certURL:       certURL,
  42  		privateKeyURL: privateKeyURL,
  43  		passphrase:    passphrase,
  44  		mux:           sync.Mutex{},
  45  		dispatcher:    dispatcher,
  46  	}
  47  }
  48  
  49  // Refresh() is failure atomic, i.e., CertificatePemRaw(), Certificate(), PrivateKeyPemRaw(), and PrivateKey() would
  50  // return their previous values if Refresh() fails.
  51  func (r *urlBasedX509CertificateRetriever) Refresh() error {
  52  	common.Debugln("Refreshing certificate")
  53  
  54  	r.mux.Lock()
  55  	defer r.mux.Unlock()
  56  
  57  	var err error
  58  
  59  	var certificatePemRaw []byte
  60  	var certificate *x509.Certificate
  61  	if certificatePemRaw, certificate, err = r.renewCertificate(r.certURL); err != nil {
  62  		return fmt.Errorf("failed to renew certificate: %s", err.Error())
  63  	}
  64  
  65  	var privateKeyPemRaw []byte
  66  	var privateKey *rsa.PrivateKey
  67  	if r.privateKeyURL != "" {
  68  		if privateKeyPemRaw, privateKey, err = r.renewPrivateKey(r.privateKeyURL, r.passphrase); err != nil {
  69  			return fmt.Errorf("failed to renew private key: %s", err.Error())
  70  		}
  71  	}
  72  
  73  	r.certificatePemRaw = certificatePemRaw
  74  	r.certificate = certificate
  75  	r.privateKeyPemRaw = privateKeyPemRaw
  76  	r.privateKey = privateKey
  77  	return nil
  78  }
  79  
  80  func (r *urlBasedX509CertificateRetriever) renewCertificate(url string) (certificatePemRaw []byte, certificate *x509.Certificate, err error) {
  81  	var body bytes.Buffer
  82  	if body, _, err = httpGet(r.dispatcher, url); err != nil {
  83  		return nil, nil, fmt.Errorf("failed to get certificate from %s: %s", url, err.Error())
  84  	}
  85  
  86  	certificatePemRaw = body.Bytes()
  87  	var block *pem.Block
  88  	block, _ = pem.Decode(certificatePemRaw)
  89  	if block == nil {
  90  		return nil, nil, fmt.Errorf("failed to parse the new certificate, not valid pem data")
  91  	}
  92  
  93  	if certificate, err = x509.ParseCertificate(block.Bytes); err != nil {
  94  		return nil, nil, fmt.Errorf("failed to parse the new certificate: %s", err.Error())
  95  	}
  96  
  97  	return certificatePemRaw, certificate, nil
  98  }
  99  
 100  func (r *urlBasedX509CertificateRetriever) renewPrivateKey(url, passphrase string) (privateKeyPemRaw []byte, privateKey *rsa.PrivateKey, err error) {
 101  	var body bytes.Buffer
 102  	if body, _, err = httpGet(r.dispatcher, url); err != nil {
 103  		return nil, nil, fmt.Errorf("failed to get private key from %s: %s", url, err.Error())
 104  	}
 105  
 106  	privateKeyPemRaw = body.Bytes()
 107  	if privateKey, err = common.PrivateKeyFromBytes(privateKeyPemRaw, &passphrase); err != nil {
 108  		return nil, nil, fmt.Errorf("failed to parse the new private key: %s", err.Error())
 109  	}
 110  
 111  	return privateKeyPemRaw, privateKey, nil
 112  }
 113  
 114  func (r *urlBasedX509CertificateRetriever) CertificatePemRaw() []byte {
 115  	r.mux.Lock()
 116  	defer r.mux.Unlock()
 117  
 118  	if r.certificatePemRaw == nil {
 119  		return nil
 120  	}
 121  
 122  	c := make([]byte, len(r.certificatePemRaw))
 123  	copy(c, r.certificatePemRaw)
 124  	return c
 125  }
 126  
 127  func (r *urlBasedX509CertificateRetriever) Certificate() *x509.Certificate {
 128  	r.mux.Lock()
 129  	defer r.mux.Unlock()
 130  
 131  	if r.certificate == nil {
 132  		return nil
 133  	}
 134  
 135  	c := *r.certificate
 136  	return &c
 137  }
 138  
 139  func (r *urlBasedX509CertificateRetriever) PrivateKeyPemRaw() []byte {
 140  	r.mux.Lock()
 141  	defer r.mux.Unlock()
 142  
 143  	if r.privateKeyPemRaw == nil {
 144  		return nil
 145  	}
 146  
 147  	c := make([]byte, len(r.privateKeyPemRaw))
 148  	copy(c, r.privateKeyPemRaw)
 149  	return c
 150  }
 151  
 152  func (r *urlBasedX509CertificateRetriever) PrivateKey() *rsa.PrivateKey {
 153  	r.mux.Lock()
 154  	defer r.mux.Unlock()
 155  
 156  	//Nil Private keys are supported as part of a certificate
 157  	if r.privateKey == nil {
 158  		return nil
 159  	}
 160  
 161  	c := *r.privateKey
 162  	return &c
 163  }
 164  
 165  // staticCertificateRetriever serves certificates from static data
 166  type staticCertificateRetriever struct {
 167  	Passphrase     []byte
 168  	CertificatePem []byte
 169  	PrivateKeyPem  []byte
 170  	certificate    *x509.Certificate
 171  	privateKey     *rsa.PrivateKey
 172  	mux            sync.Mutex
 173  }
 174  
 175  // Refresh proccess the inputs into appropiate keys and certificates
 176  func (r *staticCertificateRetriever) Refresh() error {
 177  	r.mux.Lock()
 178  	defer r.mux.Unlock()
 179  
 180  	certifcate, err := r.readCertificate()
 181  	if err != nil {
 182  		r.certificate = nil
 183  		return err
 184  	}
 185  	r.certificate = certifcate
 186  
 187  	key, err := r.readPrivateKey()
 188  	if err != nil {
 189  		r.privateKey = nil
 190  		return err
 191  	}
 192  	r.privateKey = key
 193  
 194  	return nil
 195  }
 196  
 197  func (r *staticCertificateRetriever) Certificate() *x509.Certificate {
 198  	r.mux.Lock()
 199  	defer r.mux.Unlock()
 200  
 201  	return r.certificate
 202  }
 203  
 204  func (r *staticCertificateRetriever) PrivateKey() *rsa.PrivateKey {
 205  	r.mux.Lock()
 206  	defer r.mux.Unlock()
 207  
 208  	return r.privateKey
 209  }
 210  
 211  func (r *staticCertificateRetriever) CertificatePemRaw() []byte {
 212  	r.mux.Lock()
 213  	defer r.mux.Unlock()
 214  
 215  	if r.CertificatePem == nil {
 216  		return nil
 217  	}
 218  
 219  	c := make([]byte, len(r.CertificatePem))
 220  	copy(c, r.CertificatePem)
 221  	return c
 222  }
 223  
 224  func (r *staticCertificateRetriever) PrivateKeyPemRaw() []byte {
 225  	r.mux.Lock()
 226  	defer r.mux.Unlock()
 227  
 228  	if r.PrivateKeyPem == nil {
 229  		return nil
 230  	}
 231  
 232  	c := make([]byte, len(r.PrivateKeyPem))
 233  	copy(c, r.PrivateKeyPem)
 234  	return c
 235  }
 236  
 237  func (r *staticCertificateRetriever) readCertificate() (certificate *x509.Certificate, err error) {
 238  	block, _ := pem.Decode(r.CertificatePem)
 239  	if block == nil {
 240  		return nil, fmt.Errorf("failed to parse the new certificate, not valid pem data")
 241  	}
 242  
 243  	if certificate, err = x509.ParseCertificate(block.Bytes); err != nil {
 244  		return nil, fmt.Errorf("failed to parse the new certificate: %s", err.Error())
 245  	}
 246  	return certificate, nil
 247  }
 248  
 249  func (r *staticCertificateRetriever) readPrivateKey() (*rsa.PrivateKey, error) {
 250  	if r.PrivateKeyPem == nil {
 251  		return nil, nil
 252  	}
 253  
 254  	var pass *string
 255  	if r.Passphrase == nil {
 256  		pass = nil
 257  	} else {
 258  		ss := string(r.Passphrase)
 259  		pass = &ss
 260  	}
 261  	return common.PrivateKeyFromBytes(r.PrivateKeyPem, pass)
 262  }
 263