2020-12-25 10:01:20 +02:00
|
|
|
package ocsp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/pem"
|
2021-09-28 21:49:34 +03:00
|
|
|
"io"
|
2020-12-25 10:01:20 +02:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2020-12-25 17:10:12 +02:00
|
|
|
|
|
|
|
"golang.org/x/crypto/ocsp"
|
|
|
|
|
|
|
|
"github.com/xtls/xray-core/common/platform/filesystem"
|
2020-12-25 10:01:20 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
2021-09-28 21:49:34 +03:00
|
|
|
issuerBytes, errC := io.ReadAll(resp.Body)
|
2020-12-25 10:01:20 +02:00
|
|
|
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()
|
2021-09-28 21:49:34 +03:00
|
|
|
ocspResBytes, err := io.ReadAll(req.Body)
|
2020-12-25 10:01:20 +02:00
|
|
|
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
|
|
|
|
}
|