| package com.intellij.util.net.ssl; |
| |
| import com.intellij.openapi.ui.DialogWrapper; |
| import org.apache.http.conn.ssl.X509HostnameVerifier; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.net.ssl.SSLException; |
| import javax.net.ssl.SSLSession; |
| import javax.net.ssl.SSLSocket; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.cert.Certificate; |
| import java.security.cert.X509Certificate; |
| import java.util.concurrent.Callable; |
| |
| /** |
| * @author Mikhail Golubev |
| */ /* |
| * BrowserCompatibleHostnameVerifier has all final/package-private methods, so direct |
| * inheritance from it makes no sense. Inheriting from AbstractVerifier also makes no sense, because |
| * the only method I can override verify(String, String[], String[]), and I have no access to certificate for the dialog |
| * in this case. Why the heck verify(String, X509Certificate) is final? It basically means, that I should copy half of the |
| * AbstractVerifier here, which gives me a strong feel of stupidity. |
| * |
| * I submit a bug about this problem https://issues.apache.org/jira/browse/HTTPCLIENT-1449 and it seems to be |
| * resolved in httpclient4.4. |
| */ |
| class ConfirmingHostnameVerifier implements X509HostnameVerifier { |
| private final X509HostnameVerifier myVerifier; |
| |
| public ConfirmingHostnameVerifier(@NotNull X509HostnameVerifier verifier) { |
| myVerifier = verifier; |
| } |
| |
| // Copied from httpclient 4.2 sources, read class level commentary for explanation. |
| @Override |
| public void verify(String host, SSLSocket ssl) throws IOException { |
| if (host == null) { |
| throw new NullPointerException("host to verify is null"); |
| } |
| |
| SSLSession session = ssl.getSession(); |
| if (session == null) { |
| // In our experience this only happens under IBM 1.4.x when |
| // spurious (unrelated) certificates show up in the server' |
| // chain. Hopefully this will unearth the real problem: |
| final InputStream in = ssl.getInputStream(); |
| in.available(); |
| // If ssl.getInputStream().available() didn't cause an |
| // exception, maybe at least now the session is available? |
| session = ssl.getSession(); |
| if (session == null) { |
| // If it's still null, probably a startHandshake() will |
| // unearth the real problem. |
| ssl.startHandshake(); |
| |
| // Okay, if we still haven't managed to cause an exception, |
| // might as well go for the NPE. Or maybe we're okay now? |
| session = ssl.getSession(); |
| } |
| } |
| |
| final Certificate[] certs = session.getPeerCertificates(); |
| final X509Certificate x509 = (X509Certificate)certs[0]; |
| verify(host, x509); |
| } |
| |
| @Override |
| public void verify(final String host, final X509Certificate cert) throws SSLException { |
| if (!CertificateManager.getInstance().getState().CHECK_HOSTNAME) { |
| return; |
| } |
| try { |
| myVerifier.verify(host, cert); |
| } |
| catch (SSLException e) { |
| //noinspection ConstantConditions |
| if (!accepted(host, cert)) { |
| throw e; |
| } |
| // TODO: inclusion in some kind of persistent settings |
| // Read/Write lock to protect storage? |
| } |
| } |
| |
| private static boolean accepted(final String host, final X509Certificate cert) { |
| return CertificateManager.showAcceptDialog(new Callable<DialogWrapper>() { |
| @Override |
| public DialogWrapper call() throws Exception { |
| return CertificateWarningDialog.createHostnameMismatchWarning(cert, host); |
| } |
| }); |
| } |
| |
| // Copied from httpclient 4.2 sources, read class level commentary for explanation. |
| @Override |
| public boolean verify(String host, SSLSession session) { |
| try { |
| final Certificate[] certs = session.getPeerCertificates(); |
| final X509Certificate x509 = (X509Certificate)certs[0]; |
| verify(host, x509); |
| return true; |
| } |
| catch (final SSLException e) { |
| return false; |
| } |
| } |
| |
| @Override |
| public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { |
| // actually never used, because it's only used in verify(final String host, final X509Certificate cert) |
| myVerifier.verify(host, cns, subjectAlts); |
| } |
| } |