证书中的主机名不匹配?

时间:2012-01-12 17:33:32

标签: java

我尝试使用自签名证书连接到服务器。我使用此代码接受所有证书。

public class CertificateAcceptor {

    public void initializeTrustManager() {
        try {
            SSLContext context = SSLContext.getInstance("SSL");
            context.init(null, createTrustManager(), new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }

    private TrustManager[] createTrustManager() {

    TrustManager[] trustAllCerts = new TrustManager[] {
            new X509TrustManager() {

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

                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    // leave blank to trust all clients
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    // leave blank to trust all servers
                    for (X509Certificate c : chain) {
                        System.out.println(c.toString());
                    }
                }

            }
        };
        return trustAllCerts;
    }

}

但是我得到以下错误:

javax.net.ssl.SSLException: hostname in certificate didn't match: <xyz.ch> != <localhost>
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:220)
    at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java:54)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:149)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:130)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:339)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:123)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:147)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:108)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:415)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)

我确定我的证书代码已执行,那么可能是什么问题?

5 个答案:

答案 0 :(得分:16)

ALLOW_ALL不是正确的答案。您应该使用带有ext扩展名的键盘工具来设置具有正确名称的证书:

keytool -genkeypair \
   -keystore keystore.jks \
  -dname "CN=OLEKSIYS-W3T, OU=Sun Java System Application Server, O=Sun Microsystems, L=Santa Clara, ST=California, C=US" \
  -keypass changeit \
  -storepass changeit \
  -keyalg RSA \
  -keysize 2048 \
  -alias default \
  -ext SAN=DNS:localhost,IP:127.0.0.1 \
  -validity 9999

有关详细信息,请参阅http://tersesystems.com/2014/03/23/fixing-hostname-verification/

答案 1 :(得分:7)

您可以使用SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER

SSLSocketFactory sf = new SSLSocketFactory(
    SSLContext.getInstance("TLS"),
    SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Scheme sch = new Scheme("https", 443, sf);
httpclient.getConnectionManager().getSchemeRegistry().register(sch);

HttpGet httpget = new HttpGet("https://host/");
...
...

答案 2 :(得分:2)

您是否尝试过调用setDefaultHostnameVerifier的{​​{1}}。

请参阅此链接以获取示例: Accepting a certificate for HTTPs on Android

答案 3 :(得分:1)

Prashant的答案可能不起作用,因为您还需要初始化SSLContext

我会这样做,

SSLSocketFactory sf=null ;
        SSLContext sslContext = null;
        StringWriter writer;
        try {
            sslContext = SSLContext.getInstance("TLS")  ;
            sslContext.init(null,null,null);
        } catch (NoSuchAlgorithmException e) {
            //<YourErrorHandling>
        }  catch (KeyManagementException e){
            //<YourErrorHandling>
        }

        try{
            sf = new SSLSocketFactory(sslContext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        } catch(Exception e) {
            //<YourErrorHandling>

    }
        Scheme scheme = new Scheme("https",443,sf);
        httpClient.getConnectionManager().getSchemeRegistry().register(scheme);

答案 4 :(得分:0)

我的问题是,Java SE 6或更小版本不支持SNI。这意味着,一个IP只能有一个ssl证书。但在我的服务器上,有3个不同的apis具有不同的ssl证书。 Java SE 7或更高版本支持SNI,一切正常。 (或只在服务器上运行一个api)