blob: 7aae1f1838f7c8899922db981117f90a2122e2b3 [file] [log] [blame]
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);
}
}