如何在Apache HttpClient中使用SSL客户端证书?

在Python中我使用了这样的请求:

requests.put( webdavURL, auth=(tUsername, tPassword), data=webdavFpb, verify=False, cert=("/path/to/file.pem", "/path/to/file.key")) 

非常简单。

现在我需要使用Apache HttpClient在Java中实现相同的function。 在使用HttpClient发出请求时如何传递客户端证书?

我认为主要区别在于,在java中,您通常会将密钥和证书放到密钥库中并从那里使用它。 就像你经常提到的那样,人们确实想要为它使用一个单独的库,就像提到的httpcomponents客户端一样 (就像你在python示例中使用请求库一样)。

以下是使用前面提到的库使用密钥库中的客户端证书的示例:

 import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import org.junit.Test; import javax.net.ssl.SSLContext; import java.io.InputStream; import java.security.KeyStore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; public class MyClientCertTest { private static final String KEYSTOREPATH = "/clientkeystore.jks"; // or .p12 private static final String KEYSTOREPASS = "keystorepass"; private static final String KEYPASS = "keypass"; KeyStore readStore() throws Exception { try (InputStream keyStoreStream = this.getClass().getResourceAsStream(KEYSTOREPATH)) { KeyStore keyStore = KeyStore.getInstance("JKS"); // or "PKCS12" keyStore.load(keyStoreStream, KEYSTOREPASS.toCharArray()); return keyStore; } } @Test public void readKeyStore() throws Exception { assertNotNull(readStore()); } @Test public void performClientRequest() throws Exception { SSLContext sslContext = SSLContexts.custom() .loadKeyMaterial(readStore(), KEYPASS.toCharArray()) // use null as second param if you don't have a separate key password .build(); HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build(); HttpResponse response = httpClient.execute(new HttpGet("https://slsh.iki.fi/client-certificate/protected/")); assertEquals(200, response.getStatusLine().getStatusCode()); HttpEntity entity = response.getEntity(); System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); EntityUtils.consume(entity); } } 

依赖版本的Maven pom:

   4.0.0 com.acme httptests 0.0.1-SNAPSHOT  1.8 1.8    org.apache.httpcomponents httpclient 4.5.3   junit junit 4.12 test      org.apache.maven.plugins maven-surefire-plugin 2.9   -Djavax.net.debug=all      

我还发布了一个用于测试客户端证书的简单测试页面 。


为了certificate它可以完成,下面是使用标准java api使用客户端证书的示例,没有额外的库。

 import org.junit.Test; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.security.KeyStore; public class PlainJavaHTTPS2Test { @Test public void testJKSKeyStore() throws Exception { final String KEYSTOREPATH = "clientkeystore.jks"; final char[] KEYSTOREPASS = "keystorepass".toCharArray(); final char[] KEYPASS = "keypass".toCharArray(); try (InputStream storeStream = this.getClass().getResourceAsStream(KEYSTOREPATH)) { setSSLFactories(storeStream, "JKS", KEYSTOREPASS, KEYPASS); } testPlainJavaHTTPS(); } @Test public void testP12KeyStore() throws Exception { final String KEYSTOREPATH = "clientkeystore.p12"; final char[] KEYSTOREPASS = "keystorepass".toCharArray(); final char[] KEYPASS = "keypass".toCharArray(); try (InputStream storeStream = this.getClass().getResourceAsStream(KEYSTOREPATH)) { setSSLFactories(storeStream, "PKCS12", KEYSTOREPASS, KEYPASS); } testPlainJavaHTTPS(); } private static void setSSLFactories(InputStream keyStream, String keystoreType, char[] keyStorePassword, char[] keyPassword) throws Exception { KeyStore keyStore = KeyStore.getInstance(keystoreType); keyStore.load(keyStream, keyStorePassword); KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyFactory.init(keyStore, keyPassword); KeyManager[] keyManagers = keyFactory.getKeyManagers(); SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(keyManagers, null, null); SSLContext.setDefault(sslContext); } public void testPlainJavaHTTPS() throws Exception { String httpsURL = "https://slsh.iki.fi/client-certificate/protected/"; URL myUrl = new URL(httpsURL); HttpsURLConnection conn = (HttpsURLConnection)myUrl.openConnection(); try (InputStream is = conn.getInputStream()) { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String inputLine; while ((inputLine = br.readLine()) != null) { System.out.println(inputLine); } } } } 

这里是第三个版本,代码量最少,但它依赖于以下事实:a)keystore是磁盘上的文件,而不是jar,b)密钥密码必须与密钥库密码相同。

 import org.junit.BeforeClass; import org.junit.Test; import java.net.URL; import java.io.*; import javax.net.ssl.HttpsURLConnection; public class PlainJavaHTTPSTest { @BeforeClass public static void setUp() { System.setProperty("javax.net.ssl.keyStore", "/full/path/to/clientkeystore-samepassword.jks"); System.setProperty("javax.net.ssl.keyStorePassword", "keystorepass"); } @Test public void testPlainJavaHTTPS() throws Exception { String httpsURL = "https://slsh.iki.fi/client-certificate/protected/"; URL myUrl = new URL(httpsURL); HttpsURLConnection conn = (HttpsURLConnection)myUrl.openConnection(); try (InputStream is = conn.getInputStream()) { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String inputLine; while ((inputLine = br.readLine()) != null) { System.out.println(inputLine); } } } } 

上面在代码中设置的属性当然也可以作为启动参数给出, -Djavax.net.ssl.keyStore=/full/path/to/clientkeystore-samepassword.jks-Djavax.net.ssl.keyStorePassword=keystorepass

如果要使用Apache HTTP客户端而不是Java HTTP客户端,则必须向SSLFactory提供密钥库,并配置DefaultHTTPClient以在HTTPS协议中使用它。

你可以在这里找到一个有效的例子。

我希望有所帮助。