cmd/certdump: use certlib.Fetcher.
This commit is contained in:
@@ -2,19 +2,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/dsa"
|
"crypto/dsa"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -220,122 +216,6 @@ func displayCert(cert *x509.Certificate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func displayAllCerts(in []byte, leafOnly bool) {
|
|
||||||
certs, err := certlib.ParseCertificatesPEM(in)
|
|
||||||
if err != nil {
|
|
||||||
certs, _, err = certlib.ParseCertificatesDER(in, "")
|
|
||||||
if err != nil {
|
|
||||||
_, _ = lib.Warn(err, "failed to parse certificates")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(certs) == 0 {
|
|
||||||
_, _ = lib.Warnx("no certificates found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if leafOnly {
|
|
||||||
displayCert(certs[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range certs {
|
|
||||||
displayCert(certs[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func displayAllCertsWeb(uri string, leafOnly bool) {
|
|
||||||
ci := getConnInfo(uri)
|
|
||||||
d := &tls.Dialer{Config: permissiveConfig()}
|
|
||||||
nc, err := d.DialContext(context.Background(), "tcp", ci.Addr)
|
|
||||||
if err != nil {
|
|
||||||
_, _ = lib.Warn(err, "couldn't connect to %s", ci.Addr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, ok := nc.(*tls.Conn)
|
|
||||||
if !ok {
|
|
||||||
_, _ = lib.Warnx("invalid TLS connection (not a *tls.Conn)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
state := conn.ConnectionState()
|
|
||||||
if err = conn.Close(); err != nil {
|
|
||||||
_, _ = lib.Warn(err, "couldn't close TLS connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
d = &tls.Dialer{Config: verifyConfig(ci.Host)}
|
|
||||||
nc, err = d.DialContext(context.Background(), "tcp", ci.Addr)
|
|
||||||
if err == nil {
|
|
||||||
conn, ok = nc.(*tls.Conn)
|
|
||||||
if !ok {
|
|
||||||
_, _ = lib.Warnx("invalid TLS connection (not a *tls.Conn)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = conn.VerifyHostname(ci.Host)
|
|
||||||
if err == nil {
|
|
||||||
state = conn.ConnectionState()
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
} else {
|
|
||||||
_, _ = lib.Warn(err, "TLS verification error with server name %s", ci.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(state.PeerCertificates) == 0 {
|
|
||||||
_, _ = lib.Warnx("no certificates found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if leafOnly {
|
|
||||||
displayCert(state.PeerCertificates[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(state.VerifiedChains) == 0 {
|
|
||||||
_, _ = lib.Warnx("no verified chains found; using peer chain")
|
|
||||||
for i := range state.PeerCertificates {
|
|
||||||
displayCert(state.PeerCertificates[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Fprintln(os.Stdout, "TLS chain verified successfully.")
|
|
||||||
for i := range state.VerifiedChains {
|
|
||||||
fmt.Fprintf(os.Stdout, "--- Verified certificate chain %d ---%s", i+1, "\n")
|
|
||||||
for j := range state.VerifiedChains[i] {
|
|
||||||
displayCert(state.VerifiedChains[i][j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldReadStdin(argc int, argv []string) bool {
|
|
||||||
if argc == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if argc == 1 && argv[0] == "-" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func readStdin(leafOnly bool) {
|
|
||||||
certs, err := io.ReadAll(os.Stdin)
|
|
||||||
if err != nil {
|
|
||||||
_, _ = lib.Warn(err, "couldn't read certificates from standard input")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is needed for getting certs from JSON/jq.
|
|
||||||
certs = bytes.TrimSpace(certs)
|
|
||||||
certs = bytes.ReplaceAll(certs, []byte(`\n`), []byte{0xa})
|
|
||||||
certs = bytes.Trim(certs, `"`)
|
|
||||||
displayAllCerts(certs, leafOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var leafOnly bool
|
var leafOnly bool
|
||||||
flag.BoolVar(&showHash, "d", false, "show hashes of raw DER contents")
|
flag.BoolVar(&showHash, "d", false, "show hashes of raw DER contents")
|
||||||
@@ -343,23 +223,26 @@ func main() {
|
|||||||
flag.BoolVar(&leafOnly, "l", false, "only show the leaf certificate")
|
flag.BoolVar(&leafOnly, "l", false, "only show the leaf certificate")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if shouldReadStdin(flag.NArg(), flag.Args()) {
|
opts := &certlib.FetcherOpts{
|
||||||
readStdin(leafOnly)
|
SkipVerify: true,
|
||||||
return
|
Roots: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, filename := range flag.Args() {
|
for _, filename := range flag.Args() {
|
||||||
fmt.Fprintf(os.Stdout, "--%s ---%s", filename, "\n")
|
fmt.Fprintf(os.Stdout, "--%s ---%s", filename, "\n")
|
||||||
if strings.HasPrefix(filename, "https://") {
|
certs, err := certlib.GetCertificateChain(filename, opts)
|
||||||
displayAllCertsWeb(filename, leafOnly)
|
if err != nil {
|
||||||
} else {
|
_, _ = lib.Warn(err, "couldn't read certificate")
|
||||||
in, err := os.ReadFile(filename)
|
continue
|
||||||
if err != nil {
|
}
|
||||||
_, _ = lib.Warn(err, "couldn't read certificate")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
displayAllCerts(in, leafOnly)
|
if leafOnly {
|
||||||
|
displayCert(certs[0])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range certs {
|
||||||
|
displayCert(certs[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kr/text"
|
"github.com/kr/text"
|
||||||
@@ -142,48 +140,3 @@ func dumpHex(in []byte) string {
|
|||||||
|
|
||||||
return strings.Trim(s, ":")
|
return strings.Trim(s, ":")
|
||||||
}
|
}
|
||||||
|
|
||||||
// permissiveConfig returns a maximally-accepting TLS configuration;
|
|
||||||
// the purpose is to look at the cert, not verify the security properties
|
|
||||||
// of the connection.
|
|
||||||
func permissiveConfig() *tls.Config {
|
|
||||||
return &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
} // #nosec G402
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyConfig returns a config that will verify the connection.
|
|
||||||
func verifyConfig(hostname string) *tls.Config {
|
|
||||||
return &tls.Config{
|
|
||||||
ServerName: hostname,
|
|
||||||
} // #nosec G402
|
|
||||||
}
|
|
||||||
|
|
||||||
type connInfo struct {
|
|
||||||
// The original URI provided.
|
|
||||||
URI string
|
|
||||||
|
|
||||||
// The hostname of the server.
|
|
||||||
Host string
|
|
||||||
|
|
||||||
// The port to connect on.
|
|
||||||
Port string
|
|
||||||
|
|
||||||
// The address to connect to.
|
|
||||||
Addr string
|
|
||||||
}
|
|
||||||
|
|
||||||
func getConnInfo(uri string) *connInfo {
|
|
||||||
ci := &connInfo{URI: uri}
|
|
||||||
ci.Host = uri[len("https://"):]
|
|
||||||
|
|
||||||
host, port, err := net.SplitHostPort(ci.Host)
|
|
||||||
if err != nil {
|
|
||||||
ci.Port = "443"
|
|
||||||
} else {
|
|
||||||
ci.Host = host
|
|
||||||
ci.Port = port
|
|
||||||
}
|
|
||||||
ci.Addr = net.JoinHostPort(ci.Host, ci.Port)
|
|
||||||
return ci
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user