package ocsp import ( "bytes" "crypto/x509" "encoding/pem" "io/ioutil" "net/http" "os" "golang.org/x/crypto/ocsp" "github.com/xtls/xray-core/common/platform/filesystem" ) func GetOCSPForFile(path string) ([]byte, error) { return filesystem.ReadFile(path) } func CheckOCSPFileIsNotExist(path string) bool { _, err := os.Stat(path) if err != nil { return os.IsNotExist(err) } return false } func GetOCSPStapling(cert [][]byte, path string) ([]byte, error) { ocspData, err := GetOCSPForFile(path) if err != nil { ocspData, err = GetOCSPForCert(cert) if !CheckOCSPFileIsNotExist(path) { err = os.Remove(path) if err != nil { return nil, err } } newFile, err := os.Create(path) if err != nil { return nil, err } newFile.Write(ocspData) defer newFile.Close() } return ocspData, nil } func GetOCSPForCert(cert [][]byte) ([]byte, error) { bundle := new(bytes.Buffer) for _, derBytes := range cert { err := pem.Encode(bundle, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) if err != nil { return nil, err } } pemBundle := bundle.Bytes() certificates, err := parsePEMBundle(pemBundle) if err != nil { return nil, err } issuedCert := certificates[0] if len(issuedCert.OCSPServer) == 0 { return nil, newError("no OCSP server specified in cert") } if len(certificates) == 1 { if len(issuedCert.IssuingCertificateURL) == 0 { return nil, newError("no issuing certificate URL") } resp, errC := http.Get(issuedCert.IssuingCertificateURL[0]) if errC != nil { return nil, newError("no issuing certificate URL") } defer resp.Body.Close() issuerBytes, errC := ioutil.ReadAll(resp.Body) if errC != nil { return nil, newError(errC) } issuerCert, errC := x509.ParseCertificate(issuerBytes) if errC != nil { return nil, newError(errC) } certificates = append(certificates, issuerCert) } issuerCert := certificates[1] ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil) if err != nil { return nil, err } reader := bytes.NewReader(ocspReq) req, err := http.Post(issuedCert.OCSPServer[0], "application/ocsp-request", reader) if err != nil { return nil, newError(err) } defer req.Body.Close() ocspResBytes, err := ioutil.ReadAll(req.Body) if err != nil { return nil, newError(err) } return ocspResBytes, nil } // parsePEMBundle parses a certificate bundle from top to bottom and returns // a slice of x509 certificates. This function will error if no certificates are found. func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) { var certificates []*x509.Certificate var certDERBlock *pem.Block for { certDERBlock, bundle = pem.Decode(bundle) if certDERBlock == nil { break } if certDERBlock.Type == "CERTIFICATE" { cert, err := x509.ParseCertificate(certDERBlock.Bytes) if err != nil { return nil, err } certificates = append(certificates, cert) } } if len(certificates) == 0 { return nil, newError("no certificates were found while parsing the bundle") } return certificates, nil }