[jbosstools-issues] [JBoss JIRA] (JBIDE-19594) SSL callback: provide meaningful hostname verifier, drop always accepting hostnames

Andre Dietisheim (JIRA) issues at jboss.org
Wed Apr 15 05:17:19 EDT 2015


    [ https://issues.jboss.org/browse/JBIDE-19594?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13059162#comment-13059162 ] 

Andre Dietisheim edited comment on JBIDE-19594 at 4/15/15 5:17 AM:
-------------------------------------------------------------------

An example of such a hostname verifier can be found in android land:
http://developer.android.com/reference/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.html
Another one can be found here: https://tersesystems.com/2014/03/23/fixing-hostname-verification/

{code}
class DefaultHostnameVerifier extends HostnameVerifier {
  private val logger = LoggerFactory.getLogger(getClass)

  def hostnameChecker: HostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS)

  def matchKerberos(hostname: String, principal: Principal) = HostnameChecker.`match`(hostname, principal.asInstanceOf[KerberosPrincipal])

  def isKerberos(principal: Principal): Boolean = principal != null && principal.isInstanceOf[KerberosPrincipal]

  def verify(hostname: String, session: SSLSession): Boolean = {
    logger.debug(s"verify: hostname = $hostname")

    val checker = hostnameChecker
    val result = try {
      session.getPeerCertificates match {
        case Array(cert: X509Certificate, _*) =>
          try {
            checker.`match`(hostname, cert)
            // Certificate matches hostname
            true
          } catch {
            case e: CertificateException =>
              // Certificate does not match hostname
              logger.debug("verify: Certificate does not match hostname", e)
              false
          }

        case notMatch =>
          // Peer does not have any certificates or they aren't X.509
          logger.debug(s"verify: Peer does not have any certificates: $notMatch")
          false
      }
    } catch {
      case _: SSLPeerUnverifiedException =>
        // Not using certificates for verification, try verifying the principal
        try {
          val principal = session.getPeerPrincipal
          if (isKerberos(principal)) {
            matchKerberos(hostname, principal)
          } else {
            // Can't verify principal, not Kerberos
            logger.debug(s"verify: Can't verify principal, not Kerberos")
            false
          }
        } catch {
          case e: SSLPeerUnverifiedException =>
            // Can't verify principal, no principal
            logger.debug("Can't verify principal, no principal", e)
            false
        }
    }
    logger.debug("verify: returning {}", result)
    result
  }
}
{code}

and here: http://kevinlocke.name/bits/2012/10/03/ssl-certificate-verification-in-dispatch-and-asynchttpclient/
{code}
/* An example program using AsyncHttpClient with SSL certificate verification
 *
 * To the extent possible under law, Kevin Locke has waived all copyright and
 * related or neighboring rights to this work.
 * A legal description of this waiver is available in LICENSE.txt.
 */
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.Response;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.ExecutionException;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.security.auth.kerberos.KerberosPrincipal;
import sun.security.util.HostnameChecker;

/** Implements the "MyDownloader" application */
public class MyDownloader {

    /** HostnameVerifier implementation which implements the same policy as the
     * Java built-in pre-HostnameVerifier policy.
     */
    private static class MyHostnameVerifier implements HostnameVerifier {
        /** Checks if a given hostname matches the certificate or principal of
         * a given session.
         */
        private boolean hostnameMatches(String hostname, SSLSession session) {
            HostnameChecker checker =
                HostnameChecker.getInstance(HostnameChecker.TYPE_TLS);

            boolean validCertificate = false, validPrincipal = false;
            try {
                Certificate[] peerCertificates = session.getPeerCertificates();

                if (peerCertificates.length > 0 &&
                        peerCertificates[0] instanceof X509Certificate) {
                    X509Certificate peerCertificate =
                            (X509Certificate)peerCertificates[0];

                    try {
                        checker.match(hostname, peerCertificate);
                        // Certificate matches hostname
                        validCertificate = true;
                    } catch (CertificateException ex) {
                        // Certificate does not match hostname
                    }
                } else {
                    // Peer does not have any certificates or they aren't X.509
                }
            } catch (SSLPeerUnverifiedException ex) {
                // Not using certificates for peers, try verifying the principal
                try {
                    Principal peerPrincipal = session.getPeerPrincipal();
                    if (peerPrincipal instanceof KerberosPrincipal) {
                        validPrincipal = HostnameChecker.match(hostname,
                                (KerberosPrincipal)peerPrincipal);
                    } else {
                        // Can't verify principal, not Kerberos
                    }
                } catch (SSLPeerUnverifiedException ex2) {
                    // Can't verify principal, no principal
                }
            }

            return validCertificate || validPrincipal;
        }

        public boolean verify(String hostname, SSLSession session) {
            if (hostnameMatches(hostname, session)) {
                return true;
            } else {
                // TODO: Add application-specific checks for
                // hostname/certificate match
                return false;
            }
        }
    }

    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Usage: myhttp <URL>");
        } else {
            String url = args[0];

            SSLContext context = null;
            try {
                context = SSLContext.getInstance("TLS");
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                return;
            }

            try {
                context.init(null, null, null);
            } catch (KeyManagementException e) {
                e.printStackTrace();
                return;
            }

            AsyncHttpClient client = new AsyncHttpClient(
                    new AsyncHttpClientConfig.Builder()
                        .setSSLContext(context)
                        .setHostnameVerifier(new MyHostnameVerifier())
                        .build()
                );

            Response response = null;
            try {
                response = client.prepareGet(url).execute().get();
            } catch (InterruptedException e) {
                e.printStackTrace();
                return;
            } catch (ExecutionException e) {
                e.printStackTrace();
                return;
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }

            if (response.getStatusCode() / 100 == 2) {
                try {
                    String responseBody = response.getResponseBody();
                    System.err.println("Successfully downloaded " + url);
                    System.out.println(responseBody);
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
            } else {
                System.err.println("Failure downloading " + url +
                        ": HTTP Status " + response.getStatusCode());
            }
        }
    }
}

{code}


was (Author: adietish):
An example of such a hostname verifier can be found in android land:
http://developer.android.com/reference/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.html
Another one can be found here: https://tersesystems.com/2014/03/23/fixing-hostname-verification/

{code}
class DefaultHostnameVerifier extends HostnameVerifier {
  private val logger = LoggerFactory.getLogger(getClass)

  def hostnameChecker: HostnameChecker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS)

  def matchKerberos(hostname: String, principal: Principal) = HostnameChecker.`match`(hostname, principal.asInstanceOf[KerberosPrincipal])

  def isKerberos(principal: Principal): Boolean = principal != null && principal.isInstanceOf[KerberosPrincipal]

  def verify(hostname: String, session: SSLSession): Boolean = {
    logger.debug(s"verify: hostname = $hostname")

    val checker = hostnameChecker
    val result = try {
      session.getPeerCertificates match {
        case Array(cert: X509Certificate, _*) =>
          try {
            checker.`match`(hostname, cert)
            // Certificate matches hostname
            true
          } catch {
            case e: CertificateException =>
              // Certificate does not match hostname
              logger.debug("verify: Certificate does not match hostname", e)
              false
          }

        case notMatch =>
          // Peer does not have any certificates or they aren't X.509
          logger.debug(s"verify: Peer does not have any certificates: $notMatch")
          false
      }
    } catch {
      case _: SSLPeerUnverifiedException =>
        // Not using certificates for verification, try verifying the principal
        try {
          val principal = session.getPeerPrincipal
          if (isKerberos(principal)) {
            matchKerberos(hostname, principal)
          } else {
            // Can't verify principal, not Kerberos
            logger.debug(s"verify: Can't verify principal, not Kerberos")
            false
          }
        } catch {
          case e: SSLPeerUnverifiedException =>
            // Can't verify principal, no principal
            logger.debug("Can't verify principal, no principal", e)
            false
        }
    }
    logger.debug("verify: returning {}", result)
    result
  }
}
{code}

> SSL callback: provide meaningful hostname verifier, drop always accepting hostnames
> -----------------------------------------------------------------------------------
>
>                 Key: JBIDE-19594
>                 URL: https://issues.jboss.org/browse/JBIDE-19594
>             Project: Tools (JBoss Tools)
>          Issue Type: Enhancement
>          Components: openshift
>    Affects Versions: 4.3.0.Alpha2
>            Reporter: Andre Dietisheim
>             Fix For: 4.3.0.Beta1
>
>
> We're currently using an SSL callback that will allow users to get informed and act upon "faulty" certificates (ex. self-signed ones) and mismatches btw. the host we're talking to and the one that is referenced in the ssl certificate:
> {code:title=com.openshift.client.IHttpClient.ISSLCertificateCallback}
> public interface ISSLCertificateCallback {
> 		public boolean allowCertificate(X509Certificate[] chain);
> 		public boolean allowHostname(String hostname, SSLSession session); 
> 	}
> {code}
> The callback that we are using in JBT is presenting a dialog in case the jdk cannot verify the certificate (ex. self signed certificates) and allows the user to accept/deny it.
> In case the jdk cannot verify the hostname (the host we're talking to is not matching the host that's referenced in the certificate) we're currently always accepting the hostname:
> {code:title=org.jboss.tools.openshift.express.internal.ui.wizard.connection.SSLCertificateCallback}
> 	@Override
> 	public boolean allowHostname(String hostname, SSLSession sslSession) {
> 		return true;
> 	}
> {code}
> We should find a meaningfull implementation of such a verification that does not simply always accept it. A first idea would be to present the mismatch to the user and allow it to accept/refute it.
> This issue came up JBIDE-19581 when there was no callback installed which made the hostname verification failed as in jdk. The ssl certificate used by 



--
This message was sent by Atlassian JIRA
(v6.3.11#6341)


More information about the jbosstools-issues mailing list