Java 如何在 Apache HttpClient 中使用 SSL 客户端证书?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21223084/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I use an SSL client certificate with Apache HttpClient?
提问by daniels
In Python I was using requests like this:
在 Python 中,我使用了这样的请求:
requests.put(
webdavURL,
auth=(tUsername, tPassword),
data=webdavFpb,
verify=False,
cert=("/path/to/file.pem", "/path/to/file.key"))
Easy as pie.
非常简单。
Now I need to implement the same thing in Java using Apache HttpClient. How can I pass a client certificate when making requests using HttpClient?
现在我需要使用 Apache HttpClient 在 Java 中实现同样的事情。使用 HttpClient 发出请求时如何传递客户端证书?
采纳答案by eis
I think the main difference is that in java, you usually put the key and the certificate to a key storeand use it from there. Like you mention often people do want to use a separate library for it, like mentioned httpcomponents client(just like you're using requests libraryin your python example).
我认为主要的区别在于,在 Java 中,您通常将密钥和证书放在密钥库中并从那里使用它。就像您经常提到的那样,人们确实希望为其使用单独的库,例如提到的httpcomponents 客户端(就像您在 Python 示例中使用请求库一样)。
Here's an example of using a client certificate from a key store, using the previously mentioned library:
这是使用来自密钥库的客户端证书的示例,使用前面提到的库:
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 for dependency versions:
依赖版本的 Maven pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.acme</groupId>
<artifactId>httptests</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<!-- this is not needed, but useful if you want to debug what's going
on with your connection -->
<configuration>
<argLine>-Djavax.net.debug=all</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
I've also published a simple test pagefor testing a client certificate.
我还发布了一个简单的测试页面来测试客户端证书。
Just to demonstrate that it can be done, below is an example of using client certificate just using standard java api, without extra libraries.
只是为了证明它可以做到,下面是一个使用客户端证书的例子,只使用标准的 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);
}
}
}
}
And here's a third version with least amount of code, but which relies on the fact that a) keystore is a file on disk, not within jar, and b) key password mustbe identical to keystore password.
这是代码最少的第三个版本,但它依赖于以下事实:a) 密钥库是磁盘上的文件,而不是 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);
}
}
}
}
The properties set above in code can of course be also given as startup parameters, -Djavax.net.ssl.keyStore=/full/path/to/clientkeystore-samepassword.jks
and -Djavax.net.ssl.keyStorePassword=keystorepass
.
上面代码中设置的属性当然也可以作为启动参数给出,-Djavax.net.ssl.keyStore=/full/path/to/clientkeystore-samepassword.jks
而-Djavax.net.ssl.keyStorePassword=keystorepass
.
回答by Bosko Mijin
If you want to use the Apache HTTP client instead of the Java HTTP client, you have to provide to SSLFactory your keystore and configure DefaultHTTPClient to use it in the HTTPS protocol.
如果您想使用 Apache HTTP 客户端而不是 Java HTTP 客户端,您必须向 SSLFactory 提供您的密钥库并配置 DefaultHTTPClient 以在 HTTPS 协议中使用它。
You can find a working example here.
你可以在这里找到一个工作示例。
I hope that helps.
我希望这有帮助。