github.com/xraypb/xray-core@v1.6.6/common/ocsp/ocsp.go (about)

     1  package ocsp
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/x509"
     6  	"encoding/pem"
     7  	"io"
     8  	"net/http"
     9  	"os"
    10  
    11  	"github.com/xraypb/xray-core/common/platform/filesystem"
    12  	"golang.org/x/crypto/ocsp"
    13  )
    14  
    15  func GetOCSPForFile(path string) ([]byte, error) {
    16  	return filesystem.ReadFile(path)
    17  }
    18  
    19  func CheckOCSPFileIsNotExist(path string) bool {
    20  	_, err := os.Stat(path)
    21  	if err != nil {
    22  		return os.IsNotExist(err)
    23  	}
    24  	return false
    25  }
    26  
    27  func GetOCSPStapling(cert [][]byte, path string) ([]byte, error) {
    28  	ocspData, err := GetOCSPForFile(path)
    29  	if err != nil {
    30  		ocspData, err = GetOCSPForCert(cert)
    31  		if !CheckOCSPFileIsNotExist(path) {
    32  			err = os.Remove(path)
    33  			if err != nil {
    34  				return nil, err
    35  			}
    36  		}
    37  		newFile, err := os.Create(path)
    38  		if err != nil {
    39  			return nil, err
    40  		}
    41  		newFile.Write(ocspData)
    42  		defer newFile.Close()
    43  	}
    44  	return ocspData, nil
    45  }
    46  
    47  func GetOCSPForCert(cert [][]byte) ([]byte, error) {
    48  	bundle := new(bytes.Buffer)
    49  	for _, derBytes := range cert {
    50  		err := pem.Encode(bundle, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    51  		if err != nil {
    52  			return nil, err
    53  		}
    54  	}
    55  	pemBundle := bundle.Bytes()
    56  
    57  	certificates, err := parsePEMBundle(pemBundle)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	issuedCert := certificates[0]
    62  	if len(issuedCert.OCSPServer) == 0 {
    63  		return nil, newError("no OCSP server specified in cert")
    64  	}
    65  	if len(certificates) == 1 {
    66  		if len(issuedCert.IssuingCertificateURL) == 0 {
    67  			return nil, newError("no issuing certificate URL")
    68  		}
    69  		resp, errC := http.Get(issuedCert.IssuingCertificateURL[0])
    70  		if errC != nil {
    71  			return nil, newError("no issuing certificate URL")
    72  		}
    73  		defer resp.Body.Close()
    74  
    75  		issuerBytes, errC := io.ReadAll(resp.Body)
    76  		if errC != nil {
    77  			return nil, newError(errC)
    78  		}
    79  
    80  		issuerCert, errC := x509.ParseCertificate(issuerBytes)
    81  		if errC != nil {
    82  			return nil, newError(errC)
    83  		}
    84  
    85  		certificates = append(certificates, issuerCert)
    86  	}
    87  	issuerCert := certificates[1]
    88  
    89  	ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	reader := bytes.NewReader(ocspReq)
    94  	req, err := http.Post(issuedCert.OCSPServer[0], "application/ocsp-request", reader)
    95  	if err != nil {
    96  		return nil, newError(err)
    97  	}
    98  	defer req.Body.Close()
    99  	ocspResBytes, err := io.ReadAll(req.Body)
   100  	if err != nil {
   101  		return nil, newError(err)
   102  	}
   103  	return ocspResBytes, nil
   104  }
   105  
   106  // parsePEMBundle parses a certificate bundle from top to bottom and returns
   107  // a slice of x509 certificates. This function will error if no certificates are found.
   108  func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
   109  	var certificates []*x509.Certificate
   110  	var certDERBlock *pem.Block
   111  
   112  	for {
   113  		certDERBlock, bundle = pem.Decode(bundle)
   114  		if certDERBlock == nil {
   115  			break
   116  		}
   117  
   118  		if certDERBlock.Type == "CERTIFICATE" {
   119  			cert, err := x509.ParseCertificate(certDERBlock.Bytes)
   120  			if err != nil {
   121  				return nil, err
   122  			}
   123  			certificates = append(certificates, cert)
   124  		}
   125  	}
   126  
   127  	if len(certificates) == 0 {
   128  		return nil, newError("no certificates were found while parsing the bundle")
   129  	}
   130  
   131  	return certificates, nil
   132  }