github.com/Venafi/vcert/v5@v5.10.2/pkg/playbook/app/installer/pkcs12.go (about) 1 /* 2 * Copyright 2023 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 installer 18 19 import ( 20 "crypto/x509" 21 "encoding/pem" 22 "fmt" 23 "os" 24 25 "go.uber.org/zap" 26 "software.sslmate.com/src/go-pkcs12" 27 28 "github.com/Venafi/vcert/v5/pkg/certificate" 29 "github.com/Venafi/vcert/v5/pkg/playbook/app/domain" 30 "github.com/Venafi/vcert/v5/pkg/playbook/util" 31 ) 32 33 // PKCS12Installer represents an installation that will use the PKCS12 format for the certificate bundle 34 type PKCS12Installer struct { 35 domain.Installation 36 } 37 38 // NewPKCS12Installer returns a new installer of type PKCS12 with the values defined in inst 39 func NewPKCS12Installer(inst domain.Installation) PKCS12Installer { 40 return PKCS12Installer{inst} 41 } 42 43 // Check is the method in charge of making the validations to install a new certificate: 44 // 1. Does the certificate exists? > Install if it doesn't. 45 // 2. Does the certificate is about to expire? Renew if about to expire. 46 // Returns true if the certificate needs to be installed. 47 func (r PKCS12Installer) Check(renewBefore string, _ domain.PlaybookRequest) (bool, error) { 48 zap.L().Info("checking certificate health", zap.String("format", r.Type.String()), zap.String("location", r.File)) 49 50 // Check certificate file exists 51 certExists, err := util.FileExists(r.File) 52 if err != nil { 53 return false, err 54 } 55 if !certExists { 56 return true, nil 57 } 58 59 // Load Certificate 60 cert, err := loadPKCS12(r.File, r.P12Password) 61 if err != nil { 62 return false, err 63 } 64 65 // Check certificate expiration 66 renew := needRenewal(cert, renewBefore) 67 68 return renew, nil 69 } 70 71 // Backup takes the certificate request and backs up the current version prior to overwriting 72 func (r PKCS12Installer) Backup() error { 73 zap.L().Debug("backing up certificate", zap.String("location", r.File)) 74 75 // Check certificate file exists 76 certExists, err := util.FileExists(r.File) 77 if err != nil { 78 return err 79 } 80 if !certExists { 81 zap.L().Info("new certificate location specified, no back up taken") 82 return nil 83 } 84 85 newLocation := fmt.Sprintf("%s.bak", r.File) 86 87 err = util.CopyFile(r.File, newLocation) 88 if err != nil { 89 return err 90 } 91 92 zap.L().Info("certificate backed up", zap.String("location", r.File), zap.String("backupLocation", newLocation)) 93 return err 94 } 95 96 // Install takes the certificate bundle and moves it to the location specified in the installer 97 func (r PKCS12Installer) Install(pcc certificate.PEMCollection) error { 98 zap.L().Debug("installing certificate", zap.String("location", r.File)) 99 100 if r.P12Password == "" { 101 return domain.ErrNoP12Password 102 } 103 104 content, err := packageAsPKCS12(pcc, r.P12Password, r.UseLegacyP12) 105 if err != nil { 106 zap.L().Error("could not package certificate as PKCS12") 107 return err 108 } 109 110 err = util.WriteFile(r.File, content) 111 if err != nil { 112 return err 113 } 114 115 return nil 116 } 117 118 // AfterInstallActions runs any instructions declared in the Installer on a terminal. 119 // 120 // No validations happen over the content of the AfterAction string, so caution is advised 121 func (r PKCS12Installer) AfterInstallActions() (string, error) { 122 zap.L().Debug("running after-install actions", zap.String("location", r.File)) 123 124 result, err := util.ExecuteScript(r.AfterAction) 125 return result, err 126 } 127 128 // InstallValidationActions runs any instructions declared in the Installer on a terminal and expects 129 // "0" for successful validation and "1" for a validation failure 130 // No validations happen over the content of the InstallValidation string, so caution is advised 131 func (r PKCS12Installer) InstallValidationActions() (string, error) { 132 zap.L().Debug("running install validation actions", zap.String("location", r.File)) 133 134 validationResult, err := util.ExecuteScript(r.InstallValidation) 135 if err != nil { 136 return "", err 137 } 138 139 return validationResult, err 140 } 141 142 func loadPKCS12(pkcs12File string, keyPassword string) (*x509.Certificate, error) { 143 //Open file 144 data, err := os.ReadFile(pkcs12File) 145 if err != nil { 146 zap.L().Error("could not read PKCS12 file", zap.String("location", pkcs12File)) 147 return nil, err 148 } 149 150 // Due to limitations in pkcs12 151 _, cert, _, err := pkcs12.DecodeChain(data, keyPassword) 152 if err != nil { 153 return nil, err 154 } 155 156 return cert, nil 157 } 158 159 func packageAsPKCS12(pcc certificate.PEMCollection, keyPassword string, legacyPkcs12 bool) ([]byte, error) { 160 if len(pcc.Certificate) == 0 || len(pcc.PrivateKey) == 0 { 161 return nil, fmt.Errorf("certificate and Private Key are required for PKCS12") 162 } 163 164 //Getting the certificate in bytes 165 certBlock, _ := pem.Decode([]byte(pcc.Certificate)) 166 if certBlock == nil || certBlock.Type != "CERTIFICATE" { 167 return nil, fmt.Errorf("missing Certificate PEM") 168 } 169 170 //Getting X509.Certificate object 171 cert, err := x509.ParseCertificate(certBlock.Bytes) 172 if err != nil { 173 return nil, fmt.Errorf("failed to parse Certificate bytes to X509.Certificate object") 174 } 175 176 //Getting Chain as X509.Certificate objects 177 chainList, err := getX509CertChain(pcc.Chain) 178 if err != nil { 179 return nil, err 180 } 181 182 //Getting the Private Key 183 privateKey, err := getPrivateKey(pcc.PrivateKey, keyPassword) 184 if err != nil { 185 return nil, err 186 } 187 var bytes []byte 188 189 if legacyPkcs12 { 190 bytes, err = pkcs12.Legacy.Encode(privateKey, cert, chainList, keyPassword) 191 } else { 192 bytes, err = pkcs12.Modern2023.Encode(privateKey, cert, chainList, keyPassword) 193 } 194 195 if err != nil { 196 return nil, fmt.Errorf("PKCS12 encode error: %w", err) 197 } 198 199 return bytes, nil 200 } 201 202 func getX509CertChain(chain []string) ([]*x509.Certificate, error) { 203 chainList := make([]*x509.Certificate, 0) 204 for _, chainCertStr := range chain { 205 chainBlock, _ := pem.Decode([]byte(chainCertStr)) 206 chainCert, err := x509.ParseCertificate(chainBlock.Bytes) 207 if err != nil { 208 return nil, fmt.Errorf("failed to parse Chain Certificate bytes to X509.Certificate") 209 } 210 chainList = append(chainList, chainCert) 211 } 212 213 return chainList, nil 214 }