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
|
package ca_chain
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"io"
"strings"
"github.com/sirupsen/logrus"
)
const (
pemTypeCertificate = "CERTIFICATE"
)
type pemEncoder func(out io.Writer, b *pem.Block) error
type Builder interface {
fmt.Stringer
BuildChainFromTLSConnectionState(TLS *tls.ConnectionState) error
}
func NewBuilder(logger logrus.FieldLogger) Builder {
logger = logger.
WithField("context", "certificate-chain-build")
return &defaultBuilder{
certificates: make([]*x509.Certificate, 0),
seenCertificates: make(map[string]bool),
resolver: newChainResolver(
newURLResolver(logger),
newVerifyResolver(logger),
),
encodePEM: pem.Encode,
logger: logger,
}
}
type defaultBuilder struct {
certificates []*x509.Certificate
seenCertificates map[string]bool
resolver resolver
encodePEM pemEncoder
logger logrus.FieldLogger
}
func (b *defaultBuilder) BuildChainFromTLSConnectionState(tls *tls.ConnectionState) error {
for _, verifiedChain := range tls.VerifiedChains {
b.logger.
WithField("chain-leaf", fmt.Sprintf("%v", verifiedChain)).
Debug("Processing chain")
err := b.fetchCertificatesFromVerifiedChain(verifiedChain)
if err != nil {
return fmt.Errorf("error while fetching certificates into the CA Chain: %w", err)
}
}
return nil
}
func (b *defaultBuilder) fetchCertificatesFromVerifiedChain(verifiedChain []*x509.Certificate) error {
var err error
if len(verifiedChain) < 1 {
return nil
}
verifiedChain, err = b.resolver.Resolve(verifiedChain)
if err != nil {
return fmt.Errorf("couldn't resolve certificates chain from the leaf certificate: %w", err)
}
for _, certificate := range verifiedChain {
b.addCertificate(certificate)
}
return nil
}
func (b *defaultBuilder) addCertificate(certificate *x509.Certificate) {
signature := hex.EncodeToString(certificate.Signature)
if b.seenCertificates[signature] {
return
}
b.seenCertificates[signature] = true
b.certificates = append(b.certificates, certificate)
}
func (b *defaultBuilder) String() string {
out := bytes.NewBuffer(nil)
for _, certificate := range b.certificates {
err := b.encodePEM(out, &pem.Block{Type: pemTypeCertificate, Bytes: certificate.Raw})
if err != nil {
b.logger.
WithError(err).
Warning("Failed to encode certificate from chain")
}
}
return strings.TrimSpace(out.String())
}
|