Last active
October 17, 2024 16:17
-
-
Save maraino/4dcb64cb051b17ef6d421892cb4e55a8 to your computer and use it in GitHub Desktop.
Create CRL index.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
set -e | |
# prepare copy directory | |
mkdir -p /crl/db/ | |
# clean leftovers | |
rm -f /crl/db/* | |
# make a copy of badger db | |
cp -pr /data/db /crl/ | |
# extract index.txt | |
go run listcerts.go /crl/db |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"encoding/binary" | |
"fmt" | |
"os" | |
"crypto/x509" | |
"encoding/json" | |
"encoding/pem" | |
"github.com/dgraph-io/badger" | |
"github.com/pkg/errors" | |
"strings" | |
"time" | |
) | |
// main | |
func main() { | |
db, err := badger.Open(badger.DefaultOptions(os.Args[1]).WithLogger(nil)) | |
if err != nil { | |
panic(err) | |
} | |
defer db.Close() | |
writeIndex(db, []byte("x509_certs"), "/dev/stdout") | |
} | |
func writeIndex(db *badger.DB, prefix []byte, filename string) { | |
txn := db.NewTransaction(false) | |
defer txn.Discard() | |
opts := badger.DefaultIteratorOptions | |
prefix, err := badgerEncode(prefix) | |
if err != nil { | |
panic(err) | |
} | |
iter := txn.NewIterator(opts) | |
defer iter.Close() | |
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) | |
if err != nil { | |
panic(err) | |
} | |
defer f.Close() | |
for iter.Seek(prefix); iter.ValidForPrefix(prefix); iter.Next() { | |
item := iter.Item() | |
var valCopy []byte | |
valCopy, err = item.ValueCopy(nil) | |
if err != nil { | |
panic(err) | |
} | |
if len(strings.TrimSpace(string(valCopy))) == 0 { | |
// Item is empty | |
continue | |
} | |
// read data to object | |
b, err := json.Marshal(valCopy) | |
if err != nil { | |
panic(err) | |
} | |
// make cert-data from db decodable pem | |
// json contains "" | |
r := strings.ReplaceAll(string(b), "\"", "") | |
base64 := fmt.Sprintf("-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----", r) | |
block, _ := pem.Decode([]byte(base64)) | |
if block == nil { | |
panic("failed to parse certificate PEM") | |
} | |
cert, err := x509.ParseCertificate(block.Bytes) | |
// ocsp responder needs this in HEX | |
serial := fmt.Sprintf("%X", cert.SerialNumber) | |
// step needs int to revoke by serialnumber | |
//serial := cert.SerialNumber.String() | |
if err != nil { | |
panic("failed to parse certificate: " + err.Error()) | |
} | |
var revokedAt string | |
flag := "X" | |
var obj2 *badger.Item | |
// see if cert is revoked | |
obj2, err = GetRevoked(db, []byte("revoked_x509_certs"), []byte(cert.SerialNumber.String())) | |
if err != nil { | |
// we skip errors on revoke (like not found) | |
} else { | |
// we have found a revoked cert | |
var valCopy2 []byte | |
valCopy2, err = obj2.ValueCopy(nil) | |
if err != nil { | |
panic(err) | |
} | |
if len(strings.TrimSpace(string(valCopy2))) > 0 { | |
var obj RevokeOptions | |
if err := json.Unmarshal(valCopy2, &obj); err != nil { | |
panic(err) | |
} | |
// time.RFC3339 contains - : and T | |
// index.txt wants yymmddHHMMSSZ (Z = Zulu = UTC) | |
revokedAt = fmt.Sprintf(obj.RevokedAt.UTC().Format(time.RFC3339)) | |
revokedAt = strings.ReplaceAll(string(revokedAt), ":", "") | |
revokedAt = strings.ReplaceAll(string(revokedAt), "-", "") | |
revokedAt = strings.ReplaceAll(string(revokedAt), "T", "") | |
// remove 20 from start | |
revokedAt = revokedAt[2:] | |
flag = "R" | |
} | |
} | |
// time.RFC3339 contains - : and T | |
// index.txt wants yymmddHHMMSSZ (Z = Zulu = UTC) | |
notafter := fmt.Sprintf(cert.NotAfter.UTC().Format(time.RFC3339)) | |
notafter = strings.ReplaceAll(string(notafter), ":", "") | |
notafter = strings.ReplaceAll(string(notafter), "-", "") | |
notafter = strings.ReplaceAll(string(notafter), "T", "") | |
// remove 20 from start | |
notafter = notafter[2:] | |
today := time.Now() | |
if revokedAt == "" { | |
// Cert is still valid? | |
if today.Before(cert.NotAfter) { | |
flag = "V" | |
} else { | |
flag = "E" | |
} | |
} | |
// 0) Entry type. May be "V" (valid), "R" (revoked) or "E" (expired). | |
// Note that an expired may have the type "V" because the type has | |
// not been updated. 'openssl ca updatedb' does such an update. | |
// 1) Expiration datetime. | |
// 2) Revokation datetime. This is set for any entry of the type "R". | |
// 3) Serial number. | |
// 4) File name of the certificate. This doesn't seem to be used, | |
// ever, so it's always "unknown". | |
// 5) Certificate subject name. | |
fmt.Fprintf(f, "%s\t%s\t%s\t%032s\t%s\t%s\n", flag, notafter, revokedAt, serial, "unknown", cert.Subject) | |
} | |
} | |
type RevokeOptions struct { | |
Serial string | |
Reason string | |
ReasonCode int | |
PassiveOnly bool | |
RevokedAt time.Time | |
MTLS bool | |
} | |
func GetRevoked(db *badger.DB, prefix []byte, key []byte) (item *badger.Item, rerr error) { | |
badgerKey, _ := toBadgerKey(prefix, key) | |
txn := db.NewTransaction(false) | |
defer txn.Discard() | |
opts := badger.DefaultIteratorOptions | |
_ = opts | |
item, err2 := txn.Get(badgerKey) | |
if err2 != nil { | |
return nil, err2 | |
} | |
return item, nil | |
} | |
// badgerEncode encodes a byte slice into a section of a BadgerKey. | |
// See documentation for toBadgerKey. | |
func badgerEncode(val []byte) ([]byte, error) { | |
l := len(val) | |
switch { | |
case l == 0: | |
return nil, errors.Errorf("input cannot be empty") | |
case l > 65535: | |
return nil, errors.Errorf("length of input cannot be greater than 65535") | |
default: | |
lb := new(bytes.Buffer) | |
if err := binary.Write(lb, binary.LittleEndian, uint16(l)); err != nil { | |
return nil, errors.Wrap(err, "error doing binary Write") | |
} | |
return append(lb.Bytes(), val...), nil | |
} | |
} | |
func toBadgerKey(bucket, key []byte) ([]byte, error) { | |
first, err := badgerEncode(bucket) | |
if err != nil { | |
return nil, err | |
} | |
second, err := badgerEncode(key) | |
if err != nil { | |
return nil, err | |
} | |
return append(first, second...), nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment