github.com/ztalab/ZACA@v0.0.1/pkg/caclient/cert_manager.go (about) 1 package caclient 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "crypto/x509" 7 "encoding/hex" 8 "net/http" 9 10 "github.com/ztalab/cfssl/hook" 11 12 "github.com/ztalab/cfssl/helpers" 13 "github.com/ztalab/cfssl/info" 14 15 jsoniter "github.com/json-iterator/go" 16 "github.com/pkg/errors" 17 "github.com/ztalab/cfssl/api/client" 18 "github.com/ztalab/cfssl/auth" 19 "github.com/ztalab/cfssl/signer" 20 "go.uber.org/zap" 21 ) 22 23 // CertManager Certificate manager 24 type CertManager struct { 25 logger *zap.SugaredLogger 26 apiClient *client.AuthRemote 27 profile string 28 caAddr string 29 authKey string 30 ocspFetcher OcspClient 31 // TODO Certificate storage 32 caCertTmp *x509.Certificate 33 } 34 35 // NewCertManager Create certificate management Instance 36 func (cai *CAInstance) NewCertManager() (*CertManager, error) { 37 ap, err := auth.New(cai.Conf.CFIdentity.Profiles["cfssl"]["auth-key"], nil) 38 if err != nil { 39 return nil, errors.Wrap(err, "Auth key Configuration error") 40 } 41 caAddr := cai.CaAddr 42 ocspAddr := cai.OcspAddr 43 apiClient := client.NewAuthServer(caAddr, &tls.Config{ 44 InsecureSkipVerify: true, //nolint:gosec 45 }, ap) 46 profile := cai.Conf.CFIdentity.Profiles["cfssl"]["profile"] 47 if profile == "" { 48 return nil, errors.New("profile could not be empty") 49 } 50 ocspFetcher, err := NewOcspMemCache(cai.Logger.Sugar().Named("ocsp"), ocspAddr) 51 if err != nil { 52 return nil, err 53 } 54 cm := &CertManager{ 55 logger: cai.Logger.Sugar().Named("cert-manager"), 56 apiClient: apiClient, 57 profile: profile, 58 caAddr: caAddr, 59 ocspFetcher: ocspFetcher, 60 authKey: cai.Conf.CFIdentity.Profiles["cfssl"]["auth-key"], 61 } 62 63 cm.caCertTmp, err = cm.CACert() 64 if err != nil { 65 return nil, err 66 } 67 68 return cm, nil 69 } 70 71 // SignPEM ... 72 func (cm *CertManager) SignPEM(csrPEM []byte, uniqueID string) ([]byte, error) { 73 if csrPEM == nil { 74 return nil, errors.New("empty input") 75 } 76 77 signReq := signer.SignRequest{ 78 Request: string(csrPEM), 79 Profile: cm.profile, 80 Metadata: map[string]interface{}{ 81 hook.MetadataUniqueID: uniqueID, 82 }, 83 } 84 85 csr, err := helpers.ParseCSRPEM(csrPEM) 86 if err != nil { 87 return nil, err 88 } 89 signReq.Subject = &signer.Subject{ 90 CN: csr.Subject.CommonName, 91 } 92 93 signReqBytes, err := jsoniter.Marshal(&signReq) 94 if err != nil { 95 return nil, err 96 } 97 98 cm.logger.With("req", signReq).Debug("Request for certificate") 99 100 certPEM, err := cm.apiClient.Sign(signReqBytes) 101 if err != nil { 102 cm.logger.Errorf("Request to issue certificate failed: %s", err) 103 return nil, err 104 } 105 106 return certPEM, nil 107 } 108 109 // RevokeIDGRegistryCert ... 110 func (cm *CertManager) RevokeIDGRegistryCert(certPEM []byte) error { 111 cert, err := helpers.ParseCertificatePEM(certPEM) 112 if err != nil { 113 return err 114 } 115 116 req := &RevokeRequest{ 117 Serial: cert.SerialNumber.String(), 118 AKI: hex.EncodeToString(cert.AuthorityKeyId), 119 Reason: "", // Default to 0 120 AuthKey: cm.authKey, 121 Profile: cm.profile, 122 } 123 124 reqBytes, _ := jsoniter.Marshal(req) 125 126 buf := bytes.NewBuffer(reqBytes) 127 128 resp, err := httpClient.Post(cm.caAddr+revokePath, "application/json", buf) 129 if err != nil { 130 return errors.Wrap(err, "Request error") 131 } 132 defer resp.Body.Close() 133 134 if resp.StatusCode != http.StatusOK { 135 cm.logger.With("status", resp.StatusCode).Errorf("Request error") 136 return errors.New("Request error") 137 } 138 139 return nil 140 } 141 142 // RevokeByKeyPEM ... 143 func (cm *CertManager) RevokeByKeyPEM(keyPEM, certPEM []byte) error { 144 if keyPEM == nil || certPEM == nil { 145 return errors.New("empty input") 146 } 147 priv, err := helpers.ParsePrivateKeyPEM(keyPEM) 148 if err != nil { 149 return err 150 } 151 cert, err := helpers.ParseCertificatePEM(certPEM) 152 if err != nil { 153 return err 154 } 155 return revokeCert(cm.caAddr, priv, cert) 156 } 157 158 // VerifyCertDefaultIssuer ... 159 func (cm *CertManager) VerifyCertDefaultIssuer(leafPEM []byte) error { 160 leaf, err := helpers.ParseCertificatePEM(leafPEM) 161 if err != nil { 162 return err 163 } 164 ok, err := cm.ocspFetcher.Validate(leaf, cm.caCertTmp) 165 if err != nil { 166 return err 167 } 168 if !ok { 169 return errors.New("Certificate revoked") 170 } 171 return nil 172 } 173 174 // CACert ... 175 func (cm *CertManager) CACert() (*x509.Certificate, error) { 176 reqBytes, _ := jsoniter.Marshal(&info.Req{ 177 Profile: cm.profile, 178 }) 179 resp, err := cm.apiClient.Info(reqBytes) 180 if err != nil { 181 return nil, err 182 } 183 cert, err := helpers.ParseCertificatePEM([]byte(resp.Certificate)) 184 if err != nil { 185 return nil, err 186 } 187 return cert, nil 188 } 189 190 // CACertsPEM ... 191 func (cm *CertManager) CACertsPEM() ([]byte, error) { 192 reqBytes, _ := jsoniter.Marshal(&info.Req{ 193 Profile: cm.profile, 194 }) 195 resp, err := cm.apiClient.Info(reqBytes) 196 if err != nil { 197 return nil, err 198 } 199 caCert, err := helpers.ParseCertificatePEM([]byte(resp.Certificate)) 200 if err != nil { 201 return nil, err 202 } 203 certs := make([]*x509.Certificate, 0) 204 for _, trustCert := range resp.TrustCertificates { 205 cert, err := helpers.ParseCertificatePEM([]byte(trustCert)) 206 if err != nil { 207 return nil, err 208 } 209 certs = append(certs, cert) 210 } 211 certs = append(certs, caCert) 212 213 certsPem := helpers.EncodeCertificatesPEM(certs) 214 215 return certsPem, nil 216 }