...
echo $NEWCERT | jq -r .result.private_key
OCSP & CRL
OCSP and CRL are ways of checking a certificates validity.
The cfssl crl endpoint run on port 8888: 127.0.0.1:8888/api/v1/cfssl/crl
For ocsp to run you need to run the following commands in your container:
cfssl ocspdump -db-config /config/db-pg.json > ocspdump.txt
cfssl ocspserve -port=8889 -responses=/config/ocspdump.txt -loglevel=0
These commands will need to be re-run every time a new revoke request is received.
To revoke a certificate use:
curl -d '{ "serial": "708853190752997406197199597002842275021840632879", "authority_key_id": "dd2cdb5b5b8abbe2fba81fd6c04393938f802b03", "reason": "superseded" }' http://localhost:8888/api/v1/cfssl/revoke
You can obtain the serial and authority_key_id from your database using the following SQL:
select encode(authority_key_identifier,'escape') a_key, encode(serial_number,'escape') serial, encode(status,'escape') status from certificates;
You can also obtain the serial and authority_key_id from the certinfo endpoint
- Convert your certificate into a variable: pem=$(cat my.crt | sed -z 's/\n/\\n/g')
- curl -d '{"certificate": "'"$pem"'"}' http://localhost:8888/api/v1/cfssl/certinfo
- The serial number will come back as is but the authority_key_id needs to be converted: echo "22:8B:FA:ED:8B:FF:66:E7:05:A3:08:3A:41:33:D8:01:20:CA:CC:F4"| tr '[:upper:]' '[:lower:]' | sed s'/://'g =228bfaed8bff66e705a3083a4133d80120caccf4
You can check your certificate against ocsp and crl using the following code:
Code Block | ||||
---|---|---|---|---|
| ||||
package main
import (
"bytes"
"context"
"crypto"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"flag"
"fmt"
"golang.org/x/crypto/ocsp"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"time"
)
type Cfinfo struct {
Success string `json:"success,omitempty"`
Result struct {
Certificate string `json:"certificate"`
Usages []string `json:"usages"`
Expiry string `json:"expiry"`
}
Errors []string `json:"errors"`
Messages []string `json:"messages"`
}
var cfinfo Cfinfo
type Crlinfo struct {
Success bool `json:"success,omitempty"`
Result string `json:"result,omitempty"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
}
var crlinfo Crlinfo
type ServerParameters struct {
certFile string // path to the x509 cert
issuerUrl string // webhook server port
crlUrl string // webhook server port
}
var parameters ServerParameters
func main() {
flag.StringVar(¶meters.certFile, "certFile", "", "File containing the x509 certificate")
flag.StringVar(¶meters.issuerUrl, "issuerUrl", "http://127.0.0.1:8888/api/v1/cfssl/info", "Url for retrieving the issuer certificate")
flag.StringVar(¶meters.crlUrl, "crlUrl", "http://127.0.0.1:8888/api/v1/cfssl/crl", "Url for retrieving the crl")
flag.Parse()
if parameters.certFile == "" {
flag.Usage()
os.Exit(1)
}
// read x509 certificate from PEM encoded file
cert_bytes := readFile(parameters.certFile)
cert, err := decodeCert(cert_bytes)
if err != nil {
log.Fatal(err)
}
issuer_bytes, err := getIssuer(parameters.issuerUrl) //readCert(os.Args[2])
if err != nil {
log.Fatal(err)
}
issuer, err := decodeCert(issuer_bytes)
if err != nil {
log.Fatal(err)
}
// Perform OCSP Check
fmt.Println("Checing OCSP")
status, err := checkOCSPStatus(cert, issuer)
if err != nil {
fmt.Println(err)
} else {
switch status {
case ocsp.Good:
fmt.Printf("[+] Certificate status is Good\n")
case ocsp.Revoked:
fmt.Printf("[-] Certificate status is Revoked\n")
case ocsp.Unknown:
fmt.Printf("[-] Certificate status is Unknown\n")
}
crl_bytes, err := getCrl(parameters.crlUrl) //readCert(os.Args[2])
if err != nil {
log.Fatal(err)
}
crl, err := decodeCrl(crl_bytes)
if err != nil {
log.Fatal(err)
}
fmt.Println("\nChecing CRL")
_, err = checkCRLStatus(cert, issuer, crl)
if err != nil {
log.Fatal(err)
}else{
fmt.Println("[+] Certificate status is not Revoked\n")
}
}
func checkCRLStatus(cert *x509.Certificate, issuer *x509.Certificate, crl *pkix.CertificateList) (bool, error) {
var revoked = false
// Check CRL signature
err := issuer.CheckCRLSignature(crl)
if err != nil {
return revoked, err
}
// Check CRL validity
if crl.TBSCertList.NextUpdate.Before(time.Now()) {
return revoked, fmt.Errorf("CRL is outdated")
}
// Searching for our certificate in CRL
for _, revokedCertificate := range crl.TBSCertList.RevokedCertificates {
if revokedCertificate.SerialNumber.Cmp(cert.SerialNumber) == 0 {
//Found validated certificate in list of revoked ones
revoked = true
return revoked, fmt.Errorf("[-] Certificate status is Revoked\n")
}
}
return revoked, nil
}
// CheckOCSPStatus will make an OCSP request for the provided certificate.
// If the status of the certificate is not good, then an error is returned.
func checkOCSPStatus(cert *x509.Certificate, issuer *x509.Certificate) (int, error) {
var (
ctx = context.Background()
ocspURL = cert.OCSPServer[0]
)
// Build OCSP request
buffer, err := ocsp.CreateRequest(cert, issuer, &ocsp.RequestOptions{
Hash: crypto.SHA256,
})
if err != nil {
return ocsp.Unknown, fmt.Errorf("creating ocsp request body: %w", err)
}
req, err := http.NewRequest(http.MethodPost, ocspURL, bytes.NewBuffer(buffer))
if err != nil {
return ocsp.Unknown, fmt.Errorf("creating http request: %w", err)
}
ocspUrl, err := url.Parse(ocspURL)
if err != nil {
return ocsp.Unknown, fmt.Errorf("parsing ocsp url: %w", err)
}
req.Header.Add("Content-Type", "application/ocsp-request")
req.Header.Add("Accept", "application/ocsp-response")
req.Header.Add("host", ocspUrl.Host)
req = req.WithContext(ctx)
// Make OCSP request
httpResponse, err := http.DefaultClient.Do(req)
if err != nil {
return ocsp.Unknown, fmt.Errorf("making ocsp request: %w", err)
}
defer httpResponse.Body.Close()
output, err := ioutil.ReadAll(httpResponse.Body)
if err != nil {
return ocsp.Unknown, fmt.Errorf("reading response body: %w", err)
}
// Parse response
ocspResponse, err := ocsp.ParseResponse(output, issuer)
if err != nil {
return ocsp.Unknown, fmt.Errorf("parsing ocsp response: %w", err)
}
return ocspResponse.Status, nil
}
func readFile(file string) []byte {
cert, err := ioutil.ReadFile(file)
if err != nil {
log.Fatalln(err)
}
return cert
}
func decodeCert(cert_bytes []byte) (*x509.Certificate, error) {
b, _ := pem.Decode(cert_bytes)
var cert *x509.Certificate
cert, err := x509.ParseCertificate(b.Bytes)
if err != nil {
fmt.Println("Parse Error")
return nil, fmt.Errorf("parsing certificate: %w", err)
}
return cert, err
}
func decodeCrl(cert_bytes []byte) (*pkix.CertificateList, error) {
b, _ := pem.Decode(cert_bytes)
crl, err := x509.ParseCRL(b.Bytes)
if err != nil {
return nil, fmt.Errorf("parsing crl: %w", err)
}
return crl, err
}
func getIssuer(issuerUrl string) ([]byte, error) {
var resp = &http.Response{}
values := map[string]string{"label": "intermediate"}
jsonValue, _ := json.Marshal(values)
resp, err := http.Post(issuerUrl, "application/json", bytes.NewBuffer(jsonValue))
if err != nil {
fmt.Println(err)
panic("Something wrong with the post request")
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
json.Unmarshal([]byte(body), &cfinfo)
return []byte(cfinfo.Result.Certificate), nil
}
func getCrl(crlUrl string) ([]byte, error) {
resp, err := http.Get(crlUrl)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return nil, fmt.Errorf("failed to retrieve CRL")
}
body, err := ioutil.ReadAll(resp.Body)
json.Unmarshal([]byte(body), &crlinfo)
crlString := "-----BEGIN X509 CRL-----\n" + crlinfo.Result + "\n-----END X509 CRL-----"
return []byte(crlString), err
}
|