Java 6 HTTPS客户端证书身份验证

时间:2013-08-01 07:15:39

标签: java https certificate

我尝试在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;
    }
}

1 个答案:

答案 0 :(得分:1)

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

我的Nginx服务器在同一个IP上有几个HTTPS VirtualHosts,因为Java 6没有传递确切的主机名,所以返回了默认主机。

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

listen xxx.xxx.xxx.xxx:443 default;