v2.0.0
API CHANGES: * The struct tags for marshaling/unmarshaling on TlsFlat* have changed. ADDED: * Stubs for further chain processing/associations/correlation
This commit is contained in:
parent
4cb0403e08
commit
ef5ef16de3
@ -665,6 +665,34 @@ func ParseCA(certRaw []byte) (certPool *x509.CertPool, rootCerts []*x509.Certifi
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ParseDhParams parses PEM bytes and returns parsed DH parameters.
|
||||
|
||||
Concatenated PEM files are supported.
|
||||
|
||||
TODO: Currently not fully implemented; params will always be nil.
|
||||
*/
|
||||
/*
|
||||
func ParseDhParams(dhRaw []byte) (params []*dhparam.DH, err error) {
|
||||
|
||||
var pemBlocks *PemBlocks
|
||||
|
||||
if dhRaw == nil || len(dhRaw) == 0 {
|
||||
return
|
||||
}
|
||||
if pemBlocks, err = SplitPemBlocks(dhRaw); err != nil {
|
||||
return
|
||||
}
|
||||
if pemBlocks == nil || len(*pemBlocks) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
ParseLeafCert parses PEM bytes from a (client) certificate file, iterates over a slice of
|
||||
crypto.PrivateKey (finding one that matches), and returns one (or more) tls.Certificate.
|
||||
@ -767,6 +795,49 @@ func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ParseLeafCertSimple is like ParseLeafCert, but *only* returns leaf certificates;
|
||||
no key correlation or chain building/association occurs.
|
||||
|
||||
TODO: Currently not fully implemented.
|
||||
*/
|
||||
/*
|
||||
func ParseLeafCertSimple() () {
|
||||
|
||||
// TODO
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
ParsePemBundle splits a combined PEM (also referred to as "bundled PEMs") into one or more TlsPkiChains.
|
||||
|
||||
(combinedRaw must be the PEM-encoded bytes, not the decoded contained bytes.)
|
||||
|
||||
TODO: Currently not fully implemented.
|
||||
*/
|
||||
/*
|
||||
func ParsePemBundle(combinedRaw []byte) (chains []*TlsPkiChain, err error) {
|
||||
|
||||
var roots []*x509.Certificate
|
||||
var inters []*x509.Certificate
|
||||
var keys []crypto.PrivateKey
|
||||
var certs []tls.Certificate
|
||||
|
||||
if _, roots, inters, err = ParseCA(combinedRaw); err != nil {
|
||||
return
|
||||
}
|
||||
if keys, err = ParsePrivateKey(combinedRaw); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
ParsePrivateKey parses PEM bytes to a private key. Multiple keys may be concatenated in the same file.
|
||||
|
||||
@ -807,20 +878,36 @@ func ParsePrivateKey(keyRaw []byte) (keys []crypto.PrivateKey, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
return
|
||||
}
|
||||
|
||||
// SplitPem splits a single block of bytes into one (or more) (encoding/)pem.Blocks. Currently err is not used, but is reserved for future use.
|
||||
func SplitPem(pemRaw []byte) (blocks []*pem.Block, err error) {
|
||||
|
||||
var pemBlocks *PemBlocks
|
||||
|
||||
if pemBlocks, err = SplitPemBlocks(pemRaw); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
blocks = pemBlocks.Split()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SplitPem splits a single block of bytes into one (or more) (encoding/)pem.Blocks.
|
||||
func SplitPem(pemRaw []byte) (blocks []*pem.Block, err error) {
|
||||
// SplitPemBlocks splits a single block of bytes into a PemBlocks. Currently err is not used, but is reserved for future use.
|
||||
func SplitPemBlocks(pemRaw []byte) (blocks *PemBlocks, err error) {
|
||||
|
||||
var block *pem.Block
|
||||
var nativeBlocks []*pem.Block
|
||||
var rest []byte
|
||||
|
||||
for block, rest = pem.Decode(pemRaw); block != nil; block, rest = pem.Decode(rest) {
|
||||
blocks = append(blocks, block)
|
||||
nativeBlocks = append(nativeBlocks, block)
|
||||
}
|
||||
|
||||
blocks = new(PemBlocks)
|
||||
*blocks = nativeBlocks
|
||||
|
||||
return
|
||||
}
|
||||
|
89
cryptparse/funcs_pemblocks.go
Normal file
89
cryptparse/funcs_pemblocks.go
Normal file
@ -0,0 +1,89 @@
|
||||
package cryptparse
|
||||
|
||||
import (
|
||||
`bytes`
|
||||
`encoding/pem`
|
||||
)
|
||||
|
||||
// Bytes returns a combined PEM bytes of all blocks in a PemBlocks. Any nil, empty, or otherwise invalid blocks are skipped.
|
||||
func (p *PemBlocks) Bytes() (combined []byte) {
|
||||
|
||||
var err error
|
||||
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||
|
||||
for _, block := range p.Split() {
|
||||
if block == nil || block.Bytes == nil || block.Type == "" {
|
||||
continue
|
||||
}
|
||||
// We've ruled out "contextual" errors, so we ignore it here.
|
||||
if err = pem.Encode(buf, block); err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
combined = buf.Bytes()
|
||||
_ = err
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// BytesStrict is like Bytes but is much more strict/safe (invalid/empty/nil blocks are not skipped) and will return any errors on encoding.
|
||||
func (p *PemBlocks) BytesStrict() (combined []byte, err error) {
|
||||
|
||||
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||
|
||||
for _, block := range p.Split() {
|
||||
if err = pem.Encode(buf, block); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
combined = buf.Bytes()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// BytesSplit returns separate PEM bytes of each block in a PemBlocks. Any nil, empty, or otherwise invalid blocks are skipped.
|
||||
func (p *PemBlocks) BytesSplit() (pems [][]byte) {
|
||||
|
||||
var b []byte
|
||||
|
||||
for _, block := range p.Split() {
|
||||
if block == nil || block.Bytes == nil || block.Type == "" {
|
||||
continue
|
||||
}
|
||||
// We've ruled out "contextual" errors, so we ignore it here.
|
||||
b = pem.EncodeToMemory(block)
|
||||
pems = append(pems, b)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// BytesSplitStrict is like BytesSplit but is much more strict/safe (invalid/empty/nil blocks are not skipped) and will return any errors on encoding.
|
||||
func (p *PemBlocks) BytesSplitStrict() (pems [][]byte, err error) {
|
||||
|
||||
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||
|
||||
for _, block := range p.Split() {
|
||||
buf.Reset()
|
||||
if err = pem.Encode(buf, block); err != nil {
|
||||
return
|
||||
}
|
||||
pems = append(pems, buf.Bytes())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Split returns a more primitive-friendly representation of a PemBlocks.
|
||||
func (p *PemBlocks) Split() (native []*pem.Block) {
|
||||
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
native = *p
|
||||
|
||||
return
|
||||
}
|
@ -1,30 +1,103 @@
|
||||
package cryptparse
|
||||
|
||||
import (
|
||||
`crypto`
|
||||
`crypto/tls`
|
||||
`crypto/x509`
|
||||
`encoding/pem`
|
||||
`encoding/xml`
|
||||
`net/url`
|
||||
|
||||
`github.com/Luzifer/go-dhparam`
|
||||
)
|
||||
|
||||
// PemBlocks is a combined set of multiple pem.Blocks.
|
||||
type PemBlocks []*pem.Block
|
||||
|
||||
// TlsFlat provides an easy structure to marshal/unmarshal a tls.Config from/to a data structure (JSON, XML, etc.).
|
||||
type TlsFlat struct {
|
||||
XMLName xml.Name `xml:"tlsConfig" json:"-" yaml:"-" toml:"-"`
|
||||
SniName string `json:"sni_name" xml:"sniName,attr" yaml:"SniName" toml:"SniName" required:"true" validate:"required"`
|
||||
SkipVerify bool `json:"skip_verify,omitempty" xml:"skipVerify,attr,omitempty" yaml:"SkipVerify,omitempty" toml:"SkipVerify,omitempty"`
|
||||
Certs []*TlsFlatCert `json:"certs,omitempty" xml:"certs>cert,omitempty" yaml:"Certs,omitempty" toml:"Certs,omitempty" validate:"omitempty,dive"`
|
||||
CaFiles []string `json:"ca_files,omitempty" xml:"roots>ca,omitempty" yaml:"CaFiles,omitempty" toml:"CaFiles,omitempty" validate:"omitempty,dive,filepath"`
|
||||
CipherSuites []string `json:"cipher_suites,omitempty" xml:"ciphers,omitempty" yaml:"CipherSuites,omitempty" toml:"CipherSuites,omitempty"`
|
||||
MinTlsProtocol *string `json:"min_tls_protocol,omitempty" xml:"minTlsProtocol,attr,omitempty" yaml:"MinTlsProtocol,omitempty" toml:"MinTlsProtocol,omitempty"`
|
||||
MaxTlsProtocol *string `json:"max_tls_protocol,omitempty" xml:"maxTlsProtocol,attr,omitempty" yaml:"MaxTlsProtocol,omitempty" toml:"MaxTlsProtocol,omitempty"`
|
||||
// SniName represents the expected Server Name Indicator's name. See TlsUriParamSni.
|
||||
SniName string `json:"sni_name" toml:"SNIName" yaml:"SNI Name" xml:"sniName,attr" required:"true" validate:"required"`
|
||||
// SkipVerify, if true, will bypass certificate verification. You generally should not enable this. See TlsUriParamNoVerify.
|
||||
SkipVerify bool `json:"skip_verify,omitempty" toml:"SkipVerify,omitempty" yaml:"Skip Verification,omitempty" xml:"skipVerify,attr,omitempty"`
|
||||
// Certs contains 0 or more TlsFlatCert certificate definitions. See TlsUriParamCert and TlsUriParamKey as well.
|
||||
Certs []*TlsFlatCert `json:"certs,omitempty" toml:"Certs,omitempty" yaml:"Certificates,omitempty" xml:"certs>cert,omitempty"validate:"omitempty,dive"`
|
||||
// CaFiles contains filepaths to CA certificates/"trust anchors" in PEM format. They may be combined. See TlsUriParamCa.
|
||||
CaFiles []string `json:"ca_files,omitempty" toml:"CaFiles,omitempty" yaml:"CA Files,omitempty" xml:"roots>ca,omitempty" validate:"omitempty,dive,filepath"`
|
||||
// CipherSuites represents desired ciphers/cipher suites for this TLS environment. See TlsUriParamCipher.
|
||||
CipherSuites []string `json:"cipher_suites,omitempty" toml:"CipherSuites,omitempty" yaml:"Cipher Suites,omitempty" xml:"ciphers,omitempty"`
|
||||
// Curves specifies desired cryptographic curves to be used. See TlsUriParamCurve.
|
||||
Curves []string `json:"curves,omitempty" xml:"curves>curve,omitempty" yaml:"Curves,omitempty" toml:"Curves,omitempty" validate:"omitempty,dive"`
|
||||
// MinTlsProtocol specifies the minimum TLS version. See TlsUriParamMinTls.
|
||||
MinTlsProtocol *string `json:"min_tls_protocol,omitempty" xml:"minTlsProtocol,attr,omitempty" yaml:"MinTlsProtocol,omitempty" toml:"MinTlsProtocol,omitempty"`
|
||||
// MaxTlsProtocol specifies the maximum TLS version. See TlsUriParamMaxTls.
|
||||
MaxTlsProtocol *string `json:"max_tls_protocol,omitempty" xml:"maxTlsProtocol,attr,omitempty" yaml:"MaxTlsProtocol,omitempty" toml:"MaxTlsProtocol,omitempty"`
|
||||
}
|
||||
|
||||
// TlsFlatCert represents a certificate (and, possibly, paired key).
|
||||
type TlsFlatCert struct {
|
||||
XMLName xml.Name `xml:"cert" json:"-" yaml:"-" toml:"-"`
|
||||
// KeyFile is a filepath to a PEM-encoded key file. See TlsUriParamKey.
|
||||
KeyFile *string `json:"key,omitempty" xml:"key,attr,omitempty" yaml:"Key,omitempty" toml:"Key,omitempty" validate:"omitempty,filepath"`
|
||||
// CertFile is a filepath to a PEM-encoded certificate file. See TlsUriParamCert.
|
||||
CertFile string `json:"cert" xml:",chardata" yaml:"Certificate" toml:"Certificate" required:"true" validate:"required,filepath"`
|
||||
}
|
||||
|
||||
// TlsPkiChain contains a whole X.509 PKI chain -- Root CA(s) (trust anchors) which sign Intermediate(s) which sign Certificate(s).
|
||||
type TlsPkiChain struct {
|
||||
/*
|
||||
Roots are all trust anchors/root certificates.
|
||||
|
||||
Roots are certificates that are self-signed and can issue certificates/sign CSRs.
|
||||
*/
|
||||
Roots []*x509.Certificate
|
||||
// RootsPool is an x509.CertPool representation of Roots.
|
||||
RootsPool *x509.CertPool
|
||||
/*
|
||||
Intermediates are signers that should not be trusted directly, but instead included in the verification/validation chain.
|
||||
|
||||
Intermediates are certificates that are NOT self-signed (they should be signed by at least one Roots/RootsPool)
|
||||
but CAN issue certificates/sign CSRs.
|
||||
*/
|
||||
Intermediates []*x509.Certificate
|
||||
// IntermediatesPool is an x509.CertPool representation of Intermediates.
|
||||
IntermediatesPool *x509.CertPool
|
||||
/*
|
||||
Certificates are "leaf certificates"; typically these are the certificates used directly by servers/users.
|
||||
|
||||
A certificate is considered a Certificate here if it is NOT self-signed and is NOT able to issue certificates/sign CSRs.
|
||||
*/
|
||||
Certificates []*tls.Certificate
|
||||
// CertificatesPool is an x509.CertPool representation of Certificates.
|
||||
CertificatesPool *x509.CertPool
|
||||
/*
|
||||
UnmatchedCerts contains Certificates that:
|
||||
* Do not match any of Roots/RootsPool as its signer, and/or
|
||||
* Do not match any Intermediates/IntermediatesPool as its signer, and/or
|
||||
* Does not meet requirements for Roots/RootsPool, and/or
|
||||
* Does not meet requirements for Intermediates/IntermediatesPool, and/or
|
||||
* Has no matching crypto.PrivateKey found.
|
||||
|
||||
These should generally *never* be used if they were parsed in.
|
||||
They represent "stray" certificates that have no logical chain/path found
|
||||
and are likely unusable for purposes of this environment.
|
||||
*/
|
||||
UnmatchedCerts []*x509.Certificate
|
||||
// UnmatchedCertsPool is an x509.CertPool representation of UnmatchedCerts.
|
||||
UnmatchedCertsPool *x509.CertPool
|
||||
/*
|
||||
UnmatchedKeys represent parsed private keys that have no matching corresponding certifificate.
|
||||
|
||||
These should generally *never* be used if they were parsed in.
|
||||
They represent "stray" keys that have no logical chain/path found
|
||||
and are likely unusable for purposes of this environment.
|
||||
*/
|
||||
UnmatchedKeys []crypto.PrivateKey
|
||||
// DhParams represent any found DH parameters. This will usually be empty.
|
||||
DhParams []*dhparam.DH
|
||||
}
|
||||
|
||||
type TlsUri struct {
|
||||
*url.URL
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module r00t2.io/sysutils
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/Luzifer/go-dhparam v1.2.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/g0rbe/go-chattr v1.0.1
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
|
2
go.sum
2
go.sum
@ -1,3 +1,5 @@
|
||||
github.com/Luzifer/go-dhparam v1.2.0 h1:YwDf15FTsVriTynCv1qF+1Inh6E8Dg1+28tPEA3pvFo=
|
||||
github.com/Luzifer/go-dhparam v1.2.0/go.mod h1:hnazoxBTsXnRvGXAosio70Tb1lWowquyhVdvsXdlIPc=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
Loading…
Reference in New Issue
Block a user