...

Source file src/github.com/google/certificate-transparency-go/x509util/crlcheck/crlcheck.go

Documentation: github.com/google/certificate-transparency-go/x509util/crlcheck

     1  // Copyright 2017 Google LLC. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // crlcheck is a utility to show and check the contents of certificate
    16  // revocation lists (CRLs).
    17  package main
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"io"
    23  	"net/http"
    24  	"os"
    25  	"time"
    26  
    27  	"github.com/google/certificate-transparency-go/x509"
    28  	"github.com/google/certificate-transparency-go/x509util"
    29  	"k8s.io/klog/v2"
    30  )
    31  
    32  var (
    33  	caFile      = flag.String("ca", "", "CA certificate file")
    34  	strict      = flag.Bool("strict", false, "Strict validation of CRL contents")
    35  	expectCerts = flag.Bool("cert", false, "Input files are certificates not CRLs")
    36  )
    37  
    38  func main() {
    39  	klog.InitFlags(nil)
    40  	flag.Parse()
    41  
    42  	// Build a list of possible CA certs from command line arguments.
    43  	var caCerts []*x509.Certificate
    44  	if *caFile != "" {
    45  		caDataList, err := x509util.ReadPossiblePEMFile(*caFile, "CERTIFICATE")
    46  		if err != nil {
    47  			klog.Exitf("%s: failed to read CA cert data: %v", *caFile, err)
    48  		}
    49  		for _, caData := range caDataList {
    50  			certs, err := x509.ParseCertificates(caData)
    51  			if err != nil {
    52  				klog.Errorf("%s: %v", *caFile, err)
    53  			}
    54  			if len(certs) == 0 {
    55  				klog.Errorf("%s: no certificates found", *caFile)
    56  			}
    57  			caCerts = append(caCerts, certs[0])
    58  		}
    59  	}
    60  
    61  	errored := false
    62  	for _, arg := range flag.Args() {
    63  		if *expectCerts {
    64  			if err := processCertArg(arg, caCerts); err != nil {
    65  				klog.Errorf("%s: failed to read certificate data: %v", arg, err)
    66  				errored = true
    67  			}
    68  		} else {
    69  			if err := processCRLArg(arg, caCerts); err != nil {
    70  				klog.Errorf("%s: failed to read CRL data: %v", arg, err)
    71  				errored = true
    72  			}
    73  		}
    74  	}
    75  
    76  	if errored {
    77  		os.Exit(1)
    78  	}
    79  }
    80  
    81  func processCRLArg(arg string, caCerts []*x509.Certificate) error {
    82  	dataList, err := x509util.ReadPossiblePEMURL(arg, "X509 CRL")
    83  	if err != nil {
    84  		return err
    85  	}
    86  	for _, data := range dataList {
    87  		if _, err := processCRL(data, caCerts); err != nil {
    88  			return err
    89  		}
    90  	}
    91  	return nil
    92  }
    93  
    94  func processCRL(data []byte, caCerts []*x509.Certificate) (*x509.CertificateList, error) {
    95  	certList, err := x509.ParseCertificateListDER(data)
    96  	if certList == nil {
    97  		return nil, fmt.Errorf("CRL parse error: %v", err)
    98  	}
    99  	if err != nil && *strict {
   100  		return nil, fmt.Errorf("strict CRL parse error: %v", err)
   101  	}
   102  	klog.Infof("Processing CRL:\n%s", x509util.CRLToString(certList))
   103  
   104  	verified := false
   105  	if len(caCerts) == 0 {
   106  		klog.Warningf("Skipping signature validation as no CA certs available")
   107  		verified = true
   108  	}
   109  	var verifyErr error
   110  	for _, caCert := range caCerts {
   111  		if err := caCert.CheckCertificateListSignature(certList); err != nil {
   112  			verifyErr = err
   113  		} else {
   114  			klog.Infof("CRL signature verified against CA cert %q", x509util.NameToString(caCert.Subject))
   115  			verifyErr = nil
   116  			verified = true
   117  			break
   118  		}
   119  	}
   120  	if !verified {
   121  		return nil, fmt.Errorf("verification error: %v", verifyErr)
   122  	}
   123  	return certList, nil
   124  }
   125  
   126  func processCertArg(filename string, caCerts []*x509.Certificate) error {
   127  	dataList, err := x509util.ReadPossiblePEMFile(filename, "CERTIFICATE")
   128  	if err != nil {
   129  		return err
   130  	}
   131  	if len(dataList) == 0 {
   132  		return fmt.Errorf("no certs found in %s", filename)
   133  	}
   134  
   135  	if len(caCerts) == 0 {
   136  		// No user-provided CA certs, so use any later entries in the file as possible issuers.
   137  		for i := 1; i < len(dataList); i++ {
   138  			issuer, err := x509.ParseCertificate(dataList[i])
   139  			if err != nil {
   140  				klog.Warningf("Failed to parse [%d] in chain: %v", i, err)
   141  				continue
   142  			}
   143  			klog.Infof("Treating cert [%d] with subject %q as potential issuer", i, x509util.NameToString(issuer.Subject))
   144  			caCerts = append(caCerts, issuer)
   145  		}
   146  	}
   147  	return processCert(dataList[0], caCerts)
   148  }
   149  
   150  func processCert(data []byte, caCerts []*x509.Certificate) error {
   151  	client := &http.Client{}
   152  
   153  	cert, err := x509.ParseCertificate(data)
   154  	if err != nil {
   155  		return fmt.Errorf("certificate parse error: %v", err)
   156  	}
   157  	issuer, err := x509util.GetIssuer(cert, client)
   158  	if err != nil {
   159  		klog.Warningf("Failed to retrieve issuer for cert: %v", err)
   160  	}
   161  	if issuer != nil {
   162  		klog.Infof("Using issuer %q", x509util.NameToString(issuer.Subject))
   163  		caCerts = append(caCerts, issuer)
   164  	}
   165  	expired := false
   166  	if time.Now().After(cert.NotAfter) {
   167  		klog.Errorf("Certificate is expired (since %v)", cert.NotAfter)
   168  		expired = true
   169  	}
   170  	for _, crldp := range cert.CRLDistributionPoints {
   171  		klog.Infof("Retrieving CRL from %q", crldp)
   172  		rsp, err := client.Get(crldp)
   173  		if err != nil || rsp.StatusCode != http.StatusOK {
   174  			return fmt.Errorf("failed to get CRL from %q: %v", crldp, err)
   175  		}
   176  		body, err := io.ReadAll(rsp.Body)
   177  		if err != nil {
   178  			return fmt.Errorf("failed to read CRL from %q: %v", crldp, err)
   179  		}
   180  		rsp.Body.Close()
   181  		certList, err := processCRL(body, caCerts)
   182  		if err != nil {
   183  			return err
   184  		}
   185  		if expired {
   186  			continue
   187  		}
   188  		// Check the CRL for the presence of the original cert.
   189  		for _, rev := range certList.TBSCertList.RevokedCertificates {
   190  			if rev.SerialNumber.Cmp(cert.SerialNumber) == 0 {
   191  				klog.Errorf("%s: certificate with serial number %v revoked at %v", crldp, cert.SerialNumber, rev.RevocationTime)
   192  				if rev.RevocationReason != x509.Unspecified {
   193  					klog.Errorf("  revocation reason: %s\v", x509util.RevocationReasonToString(rev.RevocationReason))
   194  				}
   195  				break
   196  			}
   197  		}
   198  	}
   199  
   200  	return nil
   201  }
   202  

View as plain text