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 }