2

我尝试在 java 6u45 上使用带有 URLConnection 的客户端证书身份验证,它返回 400 HTTP 代码,但是当我使用 java 7u25 构建相同的代码时,它工作正常并返回 OK 200。

当我尝试 Apache HttpPost 时,它对 6u45 和 7u25 都给出了 400 错误。

C:\java\test>"C:\Program Files (x86)\Java\jdk1.6.0_45\bin\javac" Test.java -cp ".;lib/*"

C:\java\test>"C:\Program Files (x86)\Java\jdk1.6.0_45\bin\java" -cp ".;lib/*" Test
Response Code 1: 400
Response Code 2: 400

C:\java\test>"C:\Program Files (x86)\Java\jdk1.7.0_25\bin\javac" Test.java -cp ".;lib/*"

C:\java\test>"C:\Program Files (x86)\Java\jdk1.7.0_25\bin\java" -cp ".;lib/*" Test
Response Code 1: 200
Response Code 2: 400

我相信至少使用 Apache HttpPost 使其在 java 6 上运行非常容易,但我做错了。

我的测试代码如下:

import java.io.*;
import java.net.*;
import java.security.*;
import javax.net.ssl.*;
import org.apache.http.*;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.*;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.BasicClientConnectionManager;
import org.apache.http.params.*;
import org.apache.http.entity.StringEntity;

public class Test {
    public static void main(String[] args) {
        String keyStorePath = "C:\\java\\keys\\client_certificate.p12";
        String keyStorePass = "someKeyStorePass";
        String trustStorePath = "C:\\java\\keys\\truststore.jks";
        String trustStorePass = "someTrustStorePath";
        String postData = "<request></request>";
        String url = "https://api.some-url.com/v2";

        SSLContext sslContext = GetSSLContext(keyStorePath, keyStorePass, trustStorePath, trustStorePass);
        PostData1(postData, url, sslContext);

        SchemeRegistry schemeRegistry = GetSchemeRegistry(keyStorePath, keyStorePass, trustStorePath, trustStorePass);
        PostData2(postData, url, schemeRegistry);
    }

    public static void PostData1(String dataToPost, String serviceUrl, SSLContext sslContext) {
        try {
            URL url = new URL(serviceUrl);

            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            HttpsURLConnection urlConn = (HttpsURLConnection)url.openConnection();
            urlConn.setDoOutput(true);
            urlConn.setDoInput(true);
            urlConn.setUseCaches(false);
            urlConn.setRequestMethod("POST");
            urlConn.setRequestProperty("Content-Type", "application/xml");
            urlConn.connect();

            PrintWriter out = new PrintWriter(urlConn.getOutputStream());
            out.println(dataToPost);
            out.close();

            System.out.println("Response Code 1: " + urlConn.getResponseCode());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void PostData2(String dataToPost, String serviceUrl, SchemeRegistry schemeRegistry) {
        try {
            HttpParams httpParams = new BasicHttpParams();
            DefaultHttpClient httpClient = new DefaultHttpClient(new BasicClientConnectionManager(schemeRegistry), httpParams);

            HttpPost httpPost = new HttpPost(serviceUrl);

            HttpEntity lEntity = new StringEntity(dataToPost, "UTF-8");
            httpPost.setEntity(lEntity);

            HttpResponse response = httpClient.execute(httpPost);

            try {
                System.out.println("Response Code 2: " + response.getStatusLine().getStatusCode());
            } finally {
                httpPost.releaseConnection();
            }

            httpClient.getConnectionManager().shutdown();

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

    public static SSLContext GetSSLContext(String clientStorePath, String clientStorePass, String trustStorePath, String trustStorePass) {
        SSLContext sslContext = null;

        try {
            KeyStore clientStore = KeyStore.getInstance("PKCS12");
            clientStore.load(new FileInputStream(clientStorePath), clientStorePass.toCharArray());

            KeyManagerFactory kmf =KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(clientStore, clientStorePass.toCharArray());
            KeyManager[] kms = kmf.getKeyManagers();

            KeyStore trustStore = KeyStore.getInstance("JKS");
            trustStore.load(new FileInputStream(trustStorePath), trustStorePass.toCharArray());

            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(trustStore);
            TrustManager[] tms = tmf.getTrustManagers();

            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(kms, tms, new SecureRandom());

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

        return sslContext;
    }

    public static SchemeRegistry GetSchemeRegistry(String clientStorePath, String clientStorePass, String trustStorePath, String trustStorePass) {
        final SchemeRegistry schemeRegistry = new SchemeRegistry();

        try {
            KeyStore clientStore = KeyStore.getInstance("PKCS12");
            clientStore.load(new FileInputStream(clientStorePath), clientStorePass.toCharArray());

            KeyStore trustStore = KeyStore.getInstance("JKS");
            trustStore.load(new FileInputStream(trustStorePath), trustStorePass.toCharArray());

            schemeRegistry.register(new Scheme("https", 443, new SSLSocketFactory(clientStore, clientStorePass, trustStore)));

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

        return schemeRegistry;
    }
}
4

1 回答 1

1

在对 HTTPS 协议流量进行深入调查后,我发现 Java 6 不支持SNI,并且该功能仅在 Java 7 中添加。

我的 Nginx 服务器在同一个 IP 上有多个 HTTPS 虚拟主机,并且由于 Java 6 没有传递确切的主机名,因此返回了默认主机。

要解决此问题,您只需将带有客户端身份验证的 VirtualHost 标记为默认值:

listen xxx.xxx.xxx.xxx:443 default;
于 2013-11-19T15:33:25.980 回答