[英]Java 6 HTTPS client certificate authentication
我嘗試在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;
}
}
在對HTTPS協議流量進行深入研究之后,我發現Java 6不支持SNI,並且僅在Java 7中添加了此功能。
我的Nginx服務器在同一IP上有多個HTTPS虛擬主機,並且由於Java 6沒有傳遞確切的主機名,因此返回了默認主機。
要解決此問題,您只需將帶有客戶端身份驗證的VirtualHost標記為默認身份驗證:
listen xxx.xxx.xxx.xxx:443 default;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.