github.com/Venafi/vcert/v5@v5.10.2/pkg/certificate/certificateCollection.go (about)

     1  /*
     2   * Copyright 2018 Venafi, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *  http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package certificate
    18  
    19  import (
    20  	"crypto"
    21  	"crypto/tls"
    22  	"crypto/x509"
    23  	"encoding/pem"
    24  	"fmt"
    25  
    26  	"github.com/Venafi/vcert/v5/pkg/verror"
    27  )
    28  
    29  // PEMCollection represents a collection of PEM data
    30  type PEMCollection struct {
    31  	Certificate string   `json:",omitempty"`
    32  	PrivateKey  string   `json:",omitempty"`
    33  	Chain       []string `json:",omitempty"`
    34  	CSR         string   `json:",omitempty"`
    35  }
    36  
    37  // NewPEMCollection creates a PEMCollection based on the data being passed in
    38  func NewPEMCollection(certificate *x509.Certificate, privateKey crypto.Signer, privateKeyPassword []byte, format ...string) (*PEMCollection, error) {
    39  	collection := PEMCollection{}
    40  	currentFormat := ""
    41  	if len(format) > 0 && format[0] != "" {
    42  		currentFormat = format[0]
    43  	}
    44  	if certificate != nil {
    45  		collection.Certificate = string(pem.EncodeToMemory(GetCertificatePEMBlock(certificate.Raw)))
    46  	}
    47  	if privateKey != nil {
    48  		var p *pem.Block
    49  		var err error
    50  		if len(privateKeyPassword) > 0 {
    51  			p, err = GetEncryptedPrivateKeyPEMBock(privateKey, privateKeyPassword, currentFormat)
    52  		} else {
    53  			p, err = GetPrivateKeyPEMBock(privateKey, currentFormat)
    54  		}
    55  		if err != nil {
    56  			return nil, err
    57  		}
    58  		collection.PrivateKey = string(pem.EncodeToMemory(p))
    59  	}
    60  	return &collection, nil
    61  }
    62  
    63  // PEMCollectionFromBytes creates a PEMCollection based on the data passed in
    64  func PEMCollectionFromBytes(certBytes []byte, chainOrder ChainOption) (*PEMCollection, error) {
    65  	var (
    66  		current    []byte
    67  		remaining  []byte
    68  		p          *pem.Block
    69  		cert       *x509.Certificate
    70  		chain      []*x509.Certificate
    71  		privPEM    string
    72  		err        error
    73  		collection *PEMCollection
    74  	)
    75  	current = certBytes
    76  
    77  	for {
    78  		p, remaining = pem.Decode(current)
    79  		if p == nil {
    80  			break
    81  		}
    82  		switch p.Type {
    83  		case "CERTIFICATE":
    84  			cert, err = x509.ParseCertificate(p.Bytes)
    85  			if err != nil {
    86  				return nil, err
    87  			}
    88  			chain = append(chain, cert)
    89  		case "RSA PRIVATE KEY", "EC PRIVATE KEY", "ENCRYPTED PRIVATE KEY", "PRIVATE KEY":
    90  			privPEM = string(current)
    91  		}
    92  		current = remaining
    93  	}
    94  
    95  	if len(chain) > 0 {
    96  		switch chainOrder {
    97  		case ChainOptionRootFirst:
    98  			collection, err = NewPEMCollection(chain[len(chain)-1], nil, nil)
    99  			if len(chain) > 1 && chainOrder != ChainOptionIgnore {
   100  				for _, caCert := range chain[:len(chain)-1] {
   101  					err = collection.AddChainElement(caCert)
   102  					if err != nil {
   103  						return nil, err
   104  					}
   105  				}
   106  			}
   107  		default:
   108  			collection, err = NewPEMCollection(chain[0], nil, nil)
   109  			if len(chain) > 1 && chainOrder != ChainOptionIgnore {
   110  				for _, caCert := range chain[1:] {
   111  					err = collection.AddChainElement(caCert)
   112  					if err != nil {
   113  						return nil, err
   114  					}
   115  				}
   116  			}
   117  		}
   118  		if err != nil {
   119  			return nil, err
   120  		}
   121  	} else {
   122  		collection = &PEMCollection{}
   123  	}
   124  	collection.PrivateKey = privPEM
   125  
   126  	return collection, nil
   127  }
   128  
   129  // AddPrivateKey adds a Private Key to the PEMCollection. Note that the collection can only contain one private key
   130  func (col *PEMCollection) AddPrivateKey(privateKey crypto.Signer, privateKeyPassword []byte, format ...string) error {
   131  
   132  	currentFormat := ""
   133  	if len(format) > 0 && format[0] != "" {
   134  		currentFormat = format[0]
   135  	}
   136  
   137  	if col.PrivateKey != "" {
   138  		return fmt.Errorf("%w: the PEM Collection can only contain one private key", verror.VcertError)
   139  	}
   140  	var p *pem.Block
   141  	var err error
   142  	if len(privateKeyPassword) > 0 {
   143  		p, err = GetEncryptedPrivateKeyPEMBock(privateKey, privateKeyPassword, currentFormat)
   144  	} else {
   145  		p, err = GetPrivateKeyPEMBock(privateKey, currentFormat)
   146  	}
   147  	if err != nil {
   148  		return err
   149  	}
   150  	col.PrivateKey = string(pem.EncodeToMemory(p))
   151  	return nil
   152  }
   153  
   154  // AddChainElement adds a chain element to the collection
   155  func (col *PEMCollection) AddChainElement(certificate *x509.Certificate) error {
   156  	if certificate == nil {
   157  		return fmt.Errorf("%w: certificate cannot be nil", verror.VcertError)
   158  	}
   159  	pemChain := col.Chain
   160  	pemChain = append(pemChain, string(pem.EncodeToMemory(GetCertificatePEMBlock(certificate.Raw))))
   161  	col.Chain = pemChain
   162  	return nil
   163  }
   164  
   165  func (col *PEMCollection) ToTLSCertificate() tls.Certificate {
   166  	cert := tls.Certificate{}
   167  	b, _ := pem.Decode([]byte(col.Certificate))
   168  	cert.Certificate = append(cert.Certificate, b.Bytes)
   169  	for _, c := range col.Chain {
   170  		b, _ := pem.Decode([]byte(c))
   171  		cert.Certificate = append(cert.Certificate, b.Bytes)
   172  	}
   173  	b, _ = pem.Decode([]byte(col.PrivateKey))
   174  
   175  	switch b.Type {
   176  	case "EC PRIVATE KEY":
   177  		cert.PrivateKey, _ = x509.ParseECPrivateKey(b.Bytes)
   178  	case "RSA PRIVATE KEY":
   179  		var privKey interface{}
   180  		privKey, err := x509.ParsePKCS1PrivateKey(b.Bytes)
   181  		if err != nil {
   182  			privKey, _ = x509.ParsePKCS8PrivateKey(b.Bytes)
   183  		}
   184  		cert.PrivateKey = privKey
   185  	}
   186  	return cert
   187  }