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,