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  }