Как обрабатывать недействительные сертификаты SSL с Apache HttpClient? - PullRequest
116 голосов
/ 01 декабря 2009

Я знаю, есть много разных вопросов и так много ответов об этой проблеме ... Но я не могу понять ...

У меня: ubuntu-9.10-desktop-amd64 + NetBeans6.7.1 установлена ​​"как есть" с выключенного компьютера. по донесению Мне нужно подключиться к некоторому сайту через HTTPS. Для этого я использую Apache HttpClient.

Из учебника я прочитал:

"После правильной установки JSSE безопасная HTTP-связь по SSL должна выглядеть как
просто как простое HTTP-соединение. "И некоторые примеры:

HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/"); 
try { 
  httpclient.executeMethod(httpget);
  System.out.println(httpget.getStatusLine());
} finally {
  httpget.releaseConnection();
}

К настоящему времени я пишу это:

HttpClient client = new HttpClient();

HttpMethod get = new GetMethod("https://mms.nw.ru");
//get.setDoAuthentication(true);

try {
    int status = client.executeMethod(get);
    System.out.println(status);

    BufferedInputStream is = new BufferedInputStream(get.getResponseBodyAsStream());
    int r=0;byte[] buf = new byte[10];
    while((r = is.read(buf)) > 0) {
        System.out.write(buf,0,r);
    }

} catch(Exception ex) {
    ex.printStackTrace();
}

В результате у меня есть набор ошибок:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1627)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:204)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:198)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:994)
        at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:142)
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:533)
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:471)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:904)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1132)
        at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:643)
        at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:78)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
        at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:828)
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2116)
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
        at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
        at simpleapachehttp.Main.main(Main.java:41)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:302)
        at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:205)
        at sun.security.validator.Validator.validate(Validator.java:235)
        at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:973)
        ... 17 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:191)
        at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:297)
        ... 23 more

Что я должен сделать, чтобы создать простейшее соединение SSL? (Вероятно, без KeyManager, Trust Manager и т. Д. Пока).

Ответы [ 18 ]

1 голос
/ 08 мая 2017

EasySSLProtocolSocketFactory доставлял мне проблемы, поэтому я в итоге реализовал свой собственный ProtocolSocketFactory.

Сначала вам необходимо зарегистрировать его:

Protocol.registerProtocol("https", new Protocol("https", new TrustAllSSLSocketFactory(), 443));

HttpClient client = new HttpClient();
...

Затем реализуйте ProtocolSocketFactory:

class TrustAllSSLSocketFactory implements ProtocolSocketFactory {

    public static final TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{
        new X509TrustManager() {
            public void checkClientTrusted(final X509Certificate[] certs, final String authType) {

            }

            public void checkServerTrusted(final X509Certificate[] certs, final String authType) {

            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        }
    };

    private TrustManager[] getTrustManager() {
        return TRUST_ALL_CERTS;
    }

    public Socket createSocket(final String host, final int port, final InetAddress clientHost,
                               final int clientPort) throws IOException {
        return getSocketFactory().createSocket(host, port, clientHost, clientPort);
    }

    @Override
    public Socket createSocket(final String host, final int port, final InetAddress localAddress,
                               final int localPort, final HttpConnectionParams params) throws IOException {
        return createSocket(host, port);
    }

    public Socket createSocket(final String host, final int port) throws IOException {
        return getSocketFactory().createSocket(host, port);
    }

    private SocketFactory getSocketFactory() throws UnknownHostException {
        TrustManager[] trustAllCerts = getTrustManager();

        try {
            SSLContext context = SSLContext.getInstance("SSL");
            context.init(null, trustAllCerts, new SecureRandom());

            final SSLSocketFactory socketFactory = context.getSocketFactory();
            HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
            return socketFactory;
        } catch (NoSuchAlgorithmException | KeyManagementException exception) {
            throw new UnknownHostException(exception.getMessage());
        }
    }
}

Примечание: это с HttpClient 3.1 и Java 8

0 голосов
/ 20 марта 2019

Я столкнулся с той же проблемой, внезапно весь мой импорт пропал. Я попытался удалить все содержимое моей папки .m2. И пытался все заново импортировать, но все равно ничего не получалось. Наконец, я открыл веб-сайт, на который IDE жаловалась, что не может загрузить его в моем браузере. И увидел сертификат, который он использовал, и увидел в моем

$ keytool -v -list  PATH_TO_JAVA_KEYSTORE

Путь к моему хранилищу ключей был /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts

этого конкретного сертификата не было.

Так что все, что вам нужно сделать, это снова поместить сертификат в хранилище ключей JAVA JVM. Это можно сделать с помощью приведенной ниже команды.

$ keytool -import -alias ANY_NAME_YOU_WANT_TO_GIVE -file PATH_TO_YOUR_CERTIFICATE -keystore PATH_OF_JAVA_KEYSTORE

Если запрашивается пароль, попробуйте пароль по умолчанию «changeit». Если вы получили ошибку разрешения при запуске вышеуказанной команды. В Windows откройте его в режиме администрирования. В Mac и Unix используйте sudo.

После того, как вы успешно добавили ключ, Вы можете просмотреть его, используя:

$ keytool -v -list  /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts 

Вы можете просмотреть только SHA-1, используя команду

$ keytool -list  /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts 
0 голосов
/ 19 января 2019

Использовал следующее вместе с DefaultTrustManager, и оно работало в httpclient как charm. Благодаря тонну!! @Kevin и любой другой участник

    SSLContext ctx = null;
    SSLConnectionSocketFactory sslsf = null;
    try {

        ctx = SSLContext.getInstance("TLS");
        ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
        SSLContext.setDefault(ctx);

        sslsf = new SSLConnectionSocketFactory(
                ctx,
                new String[] { "TLSv1" },
                null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());

    } catch (Exception e) {
        e.printStackTrace();
    }

     CloseableHttpClient client = HttpClients.custom()
            .setSSLSocketFactory(sslsf)
            .build();
0 голосов
/ 18 декабря 2018

следуйте инструкциям, приведенным ниже для Java 1.7, чтобы создать сертификат SSL с помощью файла программы InstallCert.java.

https://github.com/escline/InstallCert

вы должны перезапустить кота

0 голосов
/ 23 мая 2018

Для HttpClient мы можем сделать это:

SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
        SSLContext.setDefault(ctx);

        String uri = new StringBuilder("url").toString();

        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        };

        HttpClient client = HttpClientBuilder.create().setSSLContext(ctx)
                .setSSLHostnameVerifier(hostnameVerifier).build()
0 голосов
/ 14 октября 2016

Я использую httpclient 3.1.X, и это работает для меня

        try {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        sslContext.init(null, new TrustManager[]{trustManager}, null);
        SslContextSecureProtocolSocketFactory socketFactory = new SslContextSecureProtocolSocketFactory(sslContext,false);
        Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory) socketFactory, 443));//同样会影响到HttpUtils
    } catch (Throwable e) {
        e.printStackTrace();

}

public class SslContextSecureProtocolSocketFactory implements      SecureProtocolSocketFactory {

private SSLContext sslContext;
private boolean verifyHostname;

public SslContextSecureProtocolSocketFactory(SSLContext sslContext, boolean verifyHostname) {
    this.verifyHostname = true;
    this.sslContext = sslContext;
    this.verifyHostname = verifyHostname;
}

public SslContextSecureProtocolSocketFactory(SSLContext sslContext) {
    this(sslContext, true);
}

public SslContextSecureProtocolSocketFactory(boolean verifyHostname) {
    this((SSLContext)null, verifyHostname);
}

public SslContextSecureProtocolSocketFactory() {
    this((SSLContext)null, true);
}

public synchronized void setHostnameVerification(boolean verifyHostname) {
    this.verifyHostname = verifyHostname;
}

public synchronized boolean getHostnameVerification() {
    return this.verifyHostname;
}

public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
    SSLSocketFactory sf = this.getSslSocketFactory();
    SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port, clientHost, clientPort);
    this.verifyHostname(sslSocket);
    return sslSocket;
}

public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
    if(params == null) {
        throw new IllegalArgumentException("Parameters may not be null");
    } else {
        int timeout = params.getConnectionTimeout();
        Socket socket = null;
        SSLSocketFactory socketfactory = this.getSslSocketFactory();
        if(timeout == 0) {
            socket = socketfactory.createSocket(host, port, localAddress, localPort);
        } else {
            socket = socketfactory.createSocket();
            InetSocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
            InetSocketAddress remoteaddr = new InetSocketAddress(host, port);
            socket.bind(localaddr);
            socket.connect(remoteaddr, timeout);
        }

        this.verifyHostname((SSLSocket)socket);
        return socket;
    }
}

public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
    SSLSocketFactory sf = this.getSslSocketFactory();
    SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port);
    this.verifyHostname(sslSocket);
    return sslSocket;
}

public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    SSLSocketFactory sf = this.getSslSocketFactory();
    SSLSocket sslSocket = (SSLSocket)sf.createSocket(socket, host, port, autoClose);
    this.verifyHostname(sslSocket);
    return sslSocket;
}

private void verifyHostname(SSLSocket socket) throws SSLPeerUnverifiedException, UnknownHostException {
    synchronized(this) {
        if(!this.verifyHostname) {
            return;
        }
    }

    SSLSession session = socket.getSession();
    String hostname = session.getPeerHost();

    try {
        InetAddress.getByName(hostname);
    } catch (UnknownHostException var10) {
        throw new UnknownHostException("Could not resolve SSL sessions server hostname: " + hostname);
    }

    X509Certificate[] certs = (X509Certificate[])((X509Certificate[])session.getPeerCertificates());
    if(certs != null && certs.length != 0) {
        X500Principal subjectDN = certs[0].getSubjectX500Principal();
        List cns = this.getCNs(subjectDN);
        boolean foundHostName = false;
        Iterator i$ = cns.iterator();
        AntPathMatcher matcher  = new AntPathMatcher();
        while(i$.hasNext()) {
            String cn = (String)i$.next();
            if(matcher.match(cn.toLowerCase(),hostname.toLowerCase())) {
                foundHostName = true;
                break;
            }
        }

        if(!foundHostName) {
            throw new SSLPeerUnverifiedException("HTTPS hostname invalid: expected \'" + hostname + "\', received \'" + cns + "\'");
        }
    } else {
        throw new SSLPeerUnverifiedException("No server certificates found!");
    }
}

private List<String> getCNs(X500Principal subjectDN) {
    ArrayList cns = new ArrayList();
    StringTokenizer st = new StringTokenizer(subjectDN.getName(), ",");

    while(st.hasMoreTokens()) {
        String cnField = st.nextToken();
        if(cnField.startsWith("CN=")) {
            cns.add(cnField.substring(3));
        }
    }

    return cns;
}

protected SSLSocketFactory getSslSocketFactory() {
    SSLSocketFactory sslSocketFactory = null;
    synchronized(this) {
        if(this.sslContext != null) {
            sslSocketFactory = this.sslContext.getSocketFactory();
        }
    }

    if(sslSocketFactory == null) {
        sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
    }

    return sslSocketFactory;
}

public synchronized void setSSLContext(SSLContext sslContext) {
    this.sslContext = sslContext;
}

}

0 голосов
/ 05 марта 2012

Эта ссылка объясняет требования, которые у вас есть, шаг за шагом. Если вас не очень интересует, какой сертификат вы можете продолжить, перейдите по ссылке ниже.

Примечание. Возможно, вы захотите дважды проверить, что вы делаете, поскольку это небезопасная операция.

0 голосов
/ 28 января 2012

Использование InstallCert для генерации файла jssecacerts и выполнения -Djavax.net.ssl.trustStore=/path/to/jssecacerts отлично работал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...