package cryptparse import ( `bytes` `crypto` `crypto/tls` `crypto/x509` `errors` `fmt` `net/url` `os` `strings` `r00t2.io/sysutils/paths` ) // Normalize ensures that all specified filepaths are absolute, etc. func (t *TlsFlat) Normalize() (err error) { if t.Certs != nil { for _, c := range t.Certs { if err = paths.RealPath(&c.CertFile); err != nil { return } if c.KeyFile != nil { if err = paths.RealPath(c.KeyFile); err != nil { return } } } } if t.CaFiles != nil { for idx, _ := range t.CaFiles { if err = paths.RealPath(&t.CaFiles[idx]); err != nil { return } } } return } /* ToTlsConfig returns a tls.Config from a TlsFlat. Note that it will have Normalize called on it. Unfortunately it's not possible for this library to do the reverse, as CA certificates are not able to be extracted from an x509.CertPool. */ func (t *TlsFlat) ToTlsConfig() (tlsConf *tls.Config, err error) { var b []byte var rootCAs *x509.CertPool var intermediateCAs []*x509.Certificate var privKeys []crypto.PrivateKey var tlsCerts []tls.Certificate var parsedTlsCerts []tls.Certificate var ciphers []uint16 var curves []tls.CurveID var minVer uint16 var maxVer uint16 var buf *bytes.Buffer = new(bytes.Buffer) var srvNm string = t.SniName // Normalize any filepaths before validation. if err = t.Normalize(); err != nil { return } // And validate. if err = validate.Struct(t); err != nil { return } // CA cert(s). buf.Reset() if t.CaFiles != nil { rootCAs = x509.NewCertPool() for _, c := range t.CaFiles { if b, err = os.ReadFile(c); err != nil { if errors.Is(err, os.ErrNotExist) { err = nil continue } } buf.Write(b) } if rootCAs, _, intermediateCAs, err = ParseCA(buf.Bytes()); err != nil { return } } else { if rootCAs, err = x509.SystemCertPool(); err != nil { return } } // Keys and Certs. They are assumed to be matched. if t.Certs != nil { for _, c := range t.Certs { privKeys = nil if c.KeyFile != nil { if b, err = os.ReadFile(*c.KeyFile); err != nil { return } if privKeys, err = ParsePrivateKey(b); err != nil { return } } if b, err = os.ReadFile(c.CertFile); err != nil { return } if parsedTlsCerts, err = ParseLeafCert(b, privKeys, intermediateCAs...); err != nil { return } tlsCerts = append(tlsCerts, parsedTlsCerts...) } } // Ciphers. if t.CipherSuites != nil { ciphers = ParseTlsCiphers(strings.Join(t.CipherSuites, ",")) } // Minimum TLS Protocol Version. if t.MinTlsProtocol != nil { if minVer, err = ParseTlsVersion(*t.MinTlsProtocol); err != nil { return } } // Maximum TLS Protocol Version. if t.MaxTlsProtocol != nil { if maxVer, err = ParseTlsVersion(*t.MaxTlsProtocol); err != nil { return } } // Curves. if t.Curves != nil { curves = ParseTlsCurves(strings.Join(t.Curves, ",")) } tlsConf = &tls.Config{ Certificates: tlsCerts, RootCAs: rootCAs, ServerName: srvNm, InsecureSkipVerify: t.SkipVerify, CipherSuites: ciphers, MinVersion: minVer, MaxVersion: maxVer, CurvePreferences: curves, } return } // ToTlsUri returns a TlsUri from a TlsFlat. func (t *TlsFlat) ToTlsUri() (tlsUri *TlsUri, err error) { var u *url.URL if u, err = url.Parse(fmt.Sprintf("tls://%v/", t.SniName)); err != nil { return } // CA cert(s). if t.CaFiles != nil { for _, c := range t.CaFiles { u.Query().Add(TlsUriParamCa, c) } } // Keys and Certs. if t.Certs != nil { for _, c := range t.Certs { u.Query().Add(TlsUriParamCert, c.CertFile) if c.KeyFile != nil { u.Query().Add(TlsUriParamKey, *c.KeyFile) } } } // Enforce the SNI hostname. u.Query().Add(TlsUriParamSni, t.SniName) // Disable Verification. if t.SkipVerify { u.Query().Add(TlsUriParamNoVerify, "1") } // Ciphers. if t.CipherSuites != nil { for _, c := range t.CipherSuites { u.Query().Add(TlsUriParamCipher, c) } } // Minimum TLS Protocol Version. if t.MinTlsProtocol != nil { u.Query().Add(TlsUriParamMinTls, *t.MinTlsProtocol) } // Maximum TLS Protocol Version. if t.MaxTlsProtocol != nil { u.Query().Add(TlsUriParamMaxTls, *t.MaxTlsProtocol) } // Curves. if t.Curves != nil { for _, c := range t.Curves { u.Query().Add(TlsUriParamCurve, c) } } tlsUri = &TlsUri{ URL: u, } return }