grpc/grpc-java · error · SSLPeerUnverifiedException

Cannot verify hostname: ${host}

Error message

Cannot verify hostname: ${host}

What it means

After TLS upgrade, OkHttpTlsUpgrader verifies that the peer certificate presented on the SSLSession is valid for the target host using the configured HostnameVerifier. If verification fails, it throws SSLPeerUnverifiedException 'Cannot verify hostname: <host>'.

Source

Thrown at okhttp/src/main/java/io/grpc/okhttp/OkHttpTlsUpgrader.java:71

   */
  public static SSLSocket upgrade(SSLSocketFactory sslSocketFactory,
      @Nonnull HostnameVerifier hostnameVerifier, Socket socket, String host, int port,
      ConnectionSpec spec) throws IOException {
    Preconditions.checkNotNull(sslSocketFactory, "sslSocketFactory");
    Preconditions.checkNotNull(socket, "socket");
    Preconditions.checkNotNull(spec, "spec");
    SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
        socket, host, port, true /* auto close */);
    spec.apply(sslSocket, false);
    String negotiatedProtocol = OkHttpProtocolNegotiator.get().negotiate(
        sslSocket, host, spec.supportsTlsExtensions() ? TLS_PROTOCOLS : null);
    Preconditions.checkState(
        TLS_PROTOCOLS.contains(Protocol.get(negotiatedProtocol)),
        "Only " + TLS_PROTOCOLS + " are supported, but negotiated protocol is %s",
        negotiatedProtocol);

    if (!hostnameVerifier.verify(canonicalizeHost(host), sslSocket.getSession())) {
      throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host);
    }
    return sslSocket;
  }

  /**
   * Converts a host from URI to X509 format.
   *
   * <p>IPv6 host addresses derived from URIs are enclosed in square brackets per RFC2732, but
   * omit these brackets in X509 certificate subjectAltName extensions per RFC5280.
   *
   * @see <a href="https://www.ietf.org/rfc/rfc2732.txt">RFC2732</a>
   * @see <a href="https://tools.ietf.org/html/rfc5280#section-4.2.1.6">RFC5280</a>
   *
   * @return {@code host} in a form consistent with X509 certificates
   */
  @VisibleForTesting
  static String canonicalizeHost(String host) {
    if (host.startsWith("[") && host.endsWith("]")) {

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Obtain a certificate whose subjectAltName matches the exact host you connect to
  2. Connect using the DNS name on the certificate instead of the raw IP
  3. Register the host with your internal CA, or supply a custom HostnameVerifier for controlled environments (e.g. tests only)
  4. Verify with openssl s_client -connect host:443 -servername host that the cert matches

Example fix

// before
channel = OkHttpChannelBuilder.forAddress("10.0.0.5", 443).build();
// after
channel = OkHttpChannelBuilder.forAddress("api.example.com", 443).build();
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight check that a cert matches the host
javax.net.ssl.HttpsURLConnection c = (HttpsURLConnection) new URL("https://" + host + "/").openConnection();
c.setHostnameVerifier(javax.net.ssl.HttpsURLConnection.getDefaultHostnameVerifier());
// attempt handshake or inspect certificate SANs beforehand

Type guard

boolean certCoversHost(X509Certificate cert, String host) {
  try {
    cert.checkValidity();
    java.util.Collection<List<?>> sans = cert.getSubjectAlternativeNames();
    return sans != null && sans.stream().anyMatch(s -> s.size() > 1 && host.equalsIgnoreCase(String.valueOf(s.get(1))));
  } catch (Exception e) { return false; }
}

Try / catch

try { upgraded = OkHttpTlsUpgrader.upgrade(...); }
catch (SSLPeerUnverifiedException e) {
  log.warn("Certificate does not match host {}: {}", host, e.getMessage());
  throw e;
}

Prevention

When it happens

Trigger: HostnameVerifier.verify(canonicalizeHost(host), session) returns false during OkHttpTlsUpgrader.upgrade — typically because the server certificate's SANs do not include the connected hostname, or the hostname is an IP/literal that canonicalizes to something absent from the cert.

Common situations: Connecting via IP address to a cert issued for a DNS name; self-signed or internal CA certs without the right subjectAltName; test certificates lacking SANs; using localhost against a production cert.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/c61bc816decf608f. Report an issue: GitHub.