cmd: refactor cert utils into certlib
This commit is contained in:
141
certlib/verify/verify.go
Normal file
141
certlib/verify/verify.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package verify
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/certlib/revoke"
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
)
|
||||
|
||||
func bundleIntermediates(w io.Writer, chain []*x509.Certificate, pool *x509.CertPool, verbose bool) *x509.CertPool {
|
||||
for _, intermediate := range chain[1:] {
|
||||
if verbose {
|
||||
fmt.Fprintf(w, "[+] adding intermediate with SKI %x\n", intermediate.SubjectKeyId)
|
||||
}
|
||||
pool.AddCert(intermediate)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
type Opts struct {
|
||||
Verbose bool
|
||||
Config *tls.Config
|
||||
Intermediates *x509.CertPool
|
||||
ForceIntermediates bool
|
||||
CheckRevocation bool
|
||||
KeyUsages []x509.ExtKeyUsage
|
||||
}
|
||||
|
||||
type verifyResult struct {
|
||||
chain []*x509.Certificate
|
||||
roots *x509.CertPool
|
||||
ints *x509.CertPool
|
||||
}
|
||||
|
||||
func prepareVerification(w io.Writer, target string, opts *Opts) (*verifyResult, error) {
|
||||
var (
|
||||
roots, ints *x509.CertPool
|
||||
err error
|
||||
)
|
||||
|
||||
if opts == nil {
|
||||
opts = &Opts{
|
||||
Config: lib.StrictBaselineTLSConfig(),
|
||||
ForceIntermediates: false,
|
||||
}
|
||||
|
||||
roots, err = x509.SystemCertPool()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't load system cert pool: %w", err)
|
||||
}
|
||||
|
||||
opts.Config.RootCAs = roots
|
||||
}
|
||||
|
||||
if opts.Intermediates == nil {
|
||||
ints = x509.NewCertPool()
|
||||
} else {
|
||||
ints = opts.Intermediates.Clone()
|
||||
}
|
||||
|
||||
roots = opts.Config.RootCAs.Clone()
|
||||
|
||||
chain, err := lib.GetCertificateChain(target, opts.Config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching certificate chain: %w", err)
|
||||
}
|
||||
|
||||
if opts.Verbose {
|
||||
fmt.Fprintf(w, "[+] %s has %d certificates\n", target, len(chain))
|
||||
}
|
||||
|
||||
if len(chain) > 1 && opts.ForceIntermediates {
|
||||
ints = bundleIntermediates(w, chain, ints, opts.Verbose)
|
||||
}
|
||||
|
||||
return &verifyResult{
|
||||
chain: chain,
|
||||
roots: roots,
|
||||
ints: ints,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Chain fetches the certificate chain for a target and verifies it.
|
||||
func Chain(w io.Writer, target string, opts *Opts) ([]*x509.Certificate, error) {
|
||||
result, err := prepareVerification(w, target, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("certificate verification failed: %w", err)
|
||||
}
|
||||
|
||||
chains, err := CertWith(result.chain[0], result.roots, result.ints, opts.CheckRevocation, opts.KeyUsages...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("certificate verification failed: %w", err)
|
||||
}
|
||||
|
||||
return chains, nil
|
||||
}
|
||||
|
||||
// CertWith verifies a certificate against a set of roots and intermediates.
|
||||
func CertWith(
|
||||
cert *x509.Certificate,
|
||||
roots, ints *x509.CertPool,
|
||||
checkRevocation bool,
|
||||
keyUses ...x509.ExtKeyUsage,
|
||||
) ([]*x509.Certificate, error) {
|
||||
if len(keyUses) == 0 {
|
||||
keyUses = []x509.ExtKeyUsage{x509.ExtKeyUsageAny}
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Intermediates: ints,
|
||||
Roots: roots,
|
||||
KeyUsages: keyUses,
|
||||
}
|
||||
|
||||
chains, err := cert.Verify(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if checkRevocation {
|
||||
revoked, ok := revoke.VerifyCertificate(cert)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to check certificate revocation status")
|
||||
}
|
||||
|
||||
if revoked {
|
||||
return nil, errors.New("certificate is revoked")
|
||||
}
|
||||
}
|
||||
|
||||
if len(chains) == 0 {
|
||||
return nil, errors.New("no valid certificate chain found")
|
||||
}
|
||||
|
||||
return chains[0], nil
|
||||
}
|
||||
Reference in New Issue
Block a user