1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
package verifier
import (
"bytes"
"context"
"crypto"
"errors"
"io"
"io/ioutil"
"net/http"
"strings"
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zcrypto/x509/pkix"
"github.com/zmap/zcrypto/x509/revocation/crl"
"github.com/zmap/zcrypto/x509/revocation/ocsp"
)
const (
ocspReqContentType = "application/ocsp-request"
ocspResContentType = "application/ocsp-response"
)
// CheckOCSP - check the ocsp status of a provided certificate
// if issuer is not provided, function will attempt to fetch it
// through the AIA issuing field (which will then fail if this field is empty)
func CheckOCSP(ctx context.Context, c *x509.Certificate, issuer *x509.Certificate) (isRevoked bool, info *RevocationInfo, e error) {
if issuer == nil {
// get issuer certificate from OCSP info
if c.IssuingCertificateURL == nil {
return false, nil, errors.New("This certificate does not list an issuing party")
}
res, err := httpGet(ctx, c.IssuingCertificateURL[0])
if err != nil {
return false, nil, errors.New("failed to send HTTP Request for issuing certificate: " + err.Error())
}
issuer, err = x509.ParseCertificate(res)
if err != nil {
return false, nil, errors.New("failed to parse issuer certificate PEM: " + err.Error())
}
}
// create and send OCSP request
opts := &ocsp.RequestOptions{Hash: crypto.SHA1}
ocspRequestBytes, err := ocsp.CreateRequest(c, issuer, opts)
if err != nil {
return false, nil, errors.New("failed to construct OCSP request" + err.Error())
}
requestReader := bytes.NewReader(ocspRequestBytes)
ocspRespBytes, err := httpPost(ctx, c.OCSPServer[0], ocspReqContentType, ocspResContentType, requestReader)
if err != nil {
return false, nil, errors.New("Failed sending OCSP HTTP Request: " + err.Error())
}
ocspResp, err := ocsp.ParseResponseForCert(ocspRespBytes, c, issuer)
if err != nil {
return false, nil, errors.New("Failed to parse OCSP Response: " + err.Error())
}
isRevoked = ocspResp.IsRevoked
info = &RevocationInfo{
NextUpdate: ocspResp.NextUpdate,
}
if isRevoked {
info.RevocationTime = &ocspResp.RevokedAt
info.Reason = ocspResp.RevocationReason
}
return
}
// CheckCRL - check whether the provided certificate has been revoked through
// a CRL. If no certList is provided, function will attempt to fetch it
// through the GetCRL function. If performing repeated calls to this function,
// independently calling GetCRL and caching the list between calls to
// CheckCRL is highly recommended (otherwise the CRL will be fetched on every
// single call to CheckCRL!).
func CheckCRL(ctx context.Context, c *x509.Certificate, certList *pkix.CertificateList) (isRevoked bool, info *RevocationInfo, err error) {
if certList == nil {
certList, err = GetCRL(ctx, c.CRLDistributionPoints[0])
}
if err != nil {
return false, nil, err
}
crlData, err := crl.CheckCRLForCert(certList, c, nil)
if err != nil {
return false, nil, err
}
isRevoked = crlData.IsRevoked
info = &RevocationInfo{
NextUpdate: crlData.NextUpdate,
}
if isRevoked && crlData.CertificateEntryExtensions.Reason != nil {
info.Reason = *crlData.CertificateEntryExtensions.Reason
info.RevocationTime = &crlData.RevocationTime
}
return
}
// GetCRL - fetch and parse the CRL from the provided distrution point
func GetCRL(ctx context.Context, distributionPoint string) (*pkix.CertificateList, error) {
if strings.HasPrefix(distributionPoint, "ldap") {
return nil, errors.New("This CRL distributionPointribution point operates over LDAP - could not access")
}
crlRespBody, err := httpGet(ctx, distributionPoint)
if err != nil {
return nil, errors.New("failed to send HTTP Request for CRL: " + err.Error())
}
certList, err := x509.ParseCRL(crlRespBody)
if err != nil {
return nil, errors.New("failed to parse CRL" + err.Error())
}
return certList, nil
}
func httpGet(ctx context.Context, url string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
func httpPost(ctx context.Context, url string, contentType, accept string, reqBody io.Reader) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, reqBody)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", contentType)
req.Header.Set("Accept", accept)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
|