github.com/secure-build/gitlab-runner@v12.5.0+incompatible/helpers/tls/ca_chain/helpers.go (about)

     1  // Inspired by https://github.com/zakjan/cert-chain-resolver/blob/master/certUtil/io.go
     2  // which is licensed on a MIT license.
     3  //
     4  // Shout out to Jan Žák (http://zakjan.cz) original author of `certUtil` package and other
     5  // contributors who updated it!
     6  
     7  package ca_chain
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/x509"
    12  	"encoding/pem"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"net/http"
    16  	"strings"
    17  
    18  	"github.com/fullsailor/pkcs7"
    19  	"github.com/sirupsen/logrus"
    20  )
    21  
    22  const (
    23  	pemStart         = "-----BEGIN "
    24  	pemCertBlockType = "CERTIFICATE"
    25  )
    26  
    27  type ErrorInvalidCertificate struct {
    28  	inner            error
    29  	nonCertBlockType bool
    30  	nilBlock         bool
    31  }
    32  
    33  func (e *ErrorInvalidCertificate) Error() string {
    34  	msg := []string{"invalid certificate"}
    35  
    36  	if e.nilBlock {
    37  		msg = append(msg, "empty PEM block")
    38  	} else if e.nonCertBlockType {
    39  		msg = append(msg, "non-certificate PEM block")
    40  	} else if e.inner != nil {
    41  		msg = append(msg, e.inner.Error())
    42  	}
    43  
    44  	return strings.Join(msg, ": ")
    45  }
    46  
    47  func decodeCertificate(data []byte) (*x509.Certificate, error) {
    48  	if isPEM(data) {
    49  		block, _ := pem.Decode(data)
    50  		if block == nil {
    51  			return nil, &ErrorInvalidCertificate{nilBlock: true}
    52  		}
    53  		if block.Type != pemCertBlockType {
    54  			return nil, &ErrorInvalidCertificate{nonCertBlockType: true}
    55  		}
    56  
    57  		data = block.Bytes
    58  	}
    59  
    60  	cert, err := x509.ParseCertificate(data)
    61  	if err == nil {
    62  		return cert, nil
    63  	}
    64  
    65  	p, err := pkcs7.Parse(data)
    66  	if err == nil {
    67  		return p.Certificates[0], nil
    68  	}
    69  
    70  	return nil, &ErrorInvalidCertificate{inner: err}
    71  }
    72  
    73  func isPEM(data []byte) bool {
    74  	return bytes.HasPrefix(data, []byte(pemStart))
    75  }
    76  
    77  func isSelfSigned(cert *x509.Certificate) bool {
    78  	return cert.CheckSignatureFrom(cert) == nil
    79  }
    80  
    81  func prepareCertificateLogger(logger logrus.FieldLogger, cert *x509.Certificate) logrus.FieldLogger {
    82  	return preparePrefixedCertificateLogger(logger, cert, "")
    83  }
    84  
    85  func preparePrefixedCertificateLogger(logger logrus.FieldLogger, cert *x509.Certificate, prefix string) logrus.FieldLogger {
    86  	return logger.
    87  		WithFields(logrus.Fields{
    88  			fmt.Sprintf("%sSubject", prefix):       cert.Subject.CommonName,
    89  			fmt.Sprintf("%sIssuer", prefix):        cert.Issuer.CommonName,
    90  			fmt.Sprintf("%sSerial", prefix):        cert.SerialNumber.String(),
    91  			fmt.Sprintf("%sIssuerCertURL", prefix): cert.IssuingCertificateURL,
    92  		})
    93  }
    94  
    95  func fetchRemoteCertificate(url string) ([]byte, error) {
    96  	resp, err := http.Get(url)
    97  	if resp != nil {
    98  		defer resp.Body.Close()
    99  	}
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	data, err := ioutil.ReadAll(resp.Body)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	return data, nil
   110  }
   111  
   112  func verifyCertificate(cert *x509.Certificate) ([][]*x509.Certificate, error) {
   113  	return cert.Verify(x509.VerifyOptions{})
   114  }