From 8bfb252ca98952b3fd8547f42818cb40efe30f28 Mon Sep 17 00:00:00 2001 From: Doug MacEachern Date: Mon, 11 Mar 2024 14:34:59 -0700 Subject: [PATCH] Add support for SHA256 thumbprint based vCenter authentication vSphere in general is moving from SHA1 to SHA256 based TLS certificate thumbprints. This change allows use of SHA256 thumbprint when connecting to vCenter. govc: the about.cert command '-thumbprint' flag now outputs SHA256 instead of SHA1 --- govc/about/cert.go | 2 +- govc/test/cli.bats | 8 +++++++- object/host_certificate_info.go | 10 +--------- vim25/soap/client.go | 26 ++++++++++++++++++++------ 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/govc/about/cert.go b/govc/about/cert.go index 5a5af0f7c..8ec65d0cf 100644 --- a/govc/about/cert.go +++ b/govc/about/cert.go @@ -89,7 +89,7 @@ func (r *certResult) Write(w io.Writer) error { if r.cmd.thumbprint { u := r.cmd.Session.URL - _, err := fmt.Fprintf(w, "%s %s\n", u.Host, r.info.ThumbprintSHA1) + _, err := fmt.Fprintf(w, "%s %s\n", u.Host, r.info.ThumbprintSHA256) return err } diff --git a/govc/test/cli.bats b/govc/test/cli.bats index 0ec729101..37bb7ce96 100755 --- a/govc/test/cli.bats +++ b/govc/test/cli.bats @@ -59,7 +59,7 @@ load test_helper run govc about.cert -show assert_success - # with -k=true we get thumbprint output and exit 0 + # with -k=true we get sha256 thumbprint output and exit 0 thumbprint=$(govc about.cert -k=true -thumbprint) # with -k=true we get thumbprint output and exit 60 @@ -77,6 +77,12 @@ load test_helper run govc about -k=false -tls-known-hosts <(echo "nope nope") assert_failure + + # sha1 backwards compatibility + host=$(awk '{print $1}'<<<"$thumbprint") + sha1=$(govc about.cert -k=true -json | jq -r .thumbprintSHA1) + run govc about -k=false -tls-known-hosts <(echo "$host $sha1") + assert_success } @test "version" { diff --git a/object/host_certificate_info.go b/object/host_certificate_info.go index fd9b370eb..1a3a7fab5 100644 --- a/object/host_certificate_info.go +++ b/object/host_certificate_info.go @@ -17,7 +17,6 @@ limitations under the License. package object import ( - "crypto/sha256" "crypto/tls" "crypto/x509" "crypto/x509/pkix" @@ -58,14 +57,7 @@ func (info *HostCertificateInfo) FromCertificate(cert *x509.Certificate) *HostCe info.Subject = info.fromName(info.subjectName) info.ThumbprintSHA1 = soap.ThumbprintSHA1(cert) - - // SHA-256 for info purposes only, API fields all use SHA-1 - sum := sha256.Sum256(cert.Raw) - hex := make([]string, len(sum)) - for i, b := range sum { - hex[i] = fmt.Sprintf("%02X", b) - } - info.ThumbprintSHA256 = strings.Join(hex, ":") + info.ThumbprintSHA256 = soap.ThumbprintSHA256(cert) if info.Status == "" { info.Status = string(types.HostCertificateManagerCertificateInfoCertificateStatusUnknown) diff --git a/vim25/soap/client.go b/vim25/soap/client.go index bbf3d92f0..a253ab5b8 100644 --- a/vim25/soap/client.go +++ b/vim25/soap/client.go @@ -21,6 +21,7 @@ import ( "bytes" "context" "crypto/sha1" + "crypto/sha256" "crypto/tls" "crypto/x509" "encoding/json" @@ -387,6 +388,20 @@ func ThumbprintSHA1(cert *x509.Certificate) string { return strings.Join(hex, ":") } +// ThumbprintSHA256 returns the sha256 thumbprint of the given cert. +func ThumbprintSHA256(cert *x509.Certificate) string { + sum := sha256.Sum256(cert.Raw) + hex := make([]string, len(sum)) + for i, b := range sum { + hex[i] = fmt.Sprintf("%02X", b) + } + return strings.Join(hex, ":") +} + +func thumbprintMatches(thumbprint string, cert *x509.Certificate) bool { + return thumbprint == ThumbprintSHA256(cert) || thumbprint == ThumbprintSHA1(cert) +} + func (c *Client) dialTLSContext( ctx context.Context, network, addr string) (net.Conn, error) { @@ -418,14 +433,13 @@ func (c *Client) dialTLSContext( } cert := conn.ConnectionState().PeerCertificates[0] - peer := ThumbprintSHA1(cert) - if thumbprint != peer { - _ = conn.Close() - - return nil, fmt.Errorf("host %q thumbprint does not match %q", addr, thumbprint) + if thumbprintMatches(thumbprint, cert) { + return conn, nil } - return conn, nil + _ = conn.Close() + + return nil, fmt.Errorf("host %q thumbprint does not match %q", addr, thumbprint) } // splitHostPort is similar to net.SplitHostPort,