在Python中,我使用了这样的请求:
requests.put(
webdavURL,
auth=(tUsername, tPassword),
data=webdavFpb,
verify=False,
cert=("/path/to/file.pem", "/path/to/file.key"))
非常简单。
现在,我需要使用Apache HttpClient在Java中实现相同的功能。使用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用于依赖版本:
<?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>
我还发布了一个简单的测试页,用于测试客户端证书。
只是为了证明它是可以做到的,下面是一个示例,该示例仅使用标准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)密钥库是磁盘上的文件,而不是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
。
问题内容: 使用Websphere MQ 8.x,我们是较大环境中的一个应用程序,并且是某些数据接口的客户端。我们的应用程序是在WildFly 9上运行的Java EE应用程序,该应用程序使用资源适配器()与EAR文件一起部署在同一AS中。我们在两个方向上与MQ服务器进行交互。因此,一方面,我们有一些MDB(由于历史渊源仍是EJB 2.x格式而没有注释)列出了一些队列,并由包含激活配置属性的部署描
问题内容: 我们的系统与多个Web服务提供商进行通信。它们都是从单个Java客户端应用程序调用的。到目前为止,所有的Web服务都已通过SSL进行,但是没有一个使用客户端证书。好吧,一个新的合作伙伴正在改变这一现状。 使应用程序使用证书进行调用很容易;设置和将做到这一点。但是,现在的问题是如何制作它,使其仅在调用该特定Web服务时使用证书。我想更一般地说,我们希望能够选择要使用的客户端证书(如果有)
问题内容: 我目前在服务器具有默认的自签名SSL证书的测试环境中工作。我正在使用Restlet 2.1-RC2并实例化客户端资源,如下所示: 并在每次打来的电话中重复使用客户端。如何设置客户端,使其忽略有问题的证书。 问题答案: 正确的方法 是使用以下 方法 将此自签名证书导入客户端的信任库: 您可以直接在可能缺乏灵活性的JRE信任库()中进行操作,也可以在自己的文件副本中进行操作,然后将该文件设
问题内容: 在我的Java应用程序中,我需要使用SSL连接到同一主机,但每次都使用不同的证书。我需要使用其他证书的原因是,远程站点使用嵌入在证书中的用户ID属性来标识客户端。 这是一个在3个不同的操作系统上运行的服务器应用程序,我需要能够在不重新启动进程的情况下切换证书。 问题答案: SSL可以向客户端提示要显示的证书。这 可能 使您可以使用一个具有多个身份的密钥存储,但是,不幸的是,大多数服务器
问题内容: 我有以下代码使用SSL将服务器与客户端连接,现在我想添加客户端身份验证: (我有一个服务器密钥库(JCEKS类型)和一个客户端密钥库(JKS类型),服务器使用了一个信任库(证书),在其中我导入了两个证书,因为我也想使用此信任库进行客户端身份验证) 客户代码: 服务器代码: 在此先感谢您的帮助。 编辑:我在服务器端添加此代码: 但是,如果我删除cacerts中的客户端证书,则连接不会给我
问题内容: 我想要求基于Java 1.7内置HttpsServer的服务器进行客户端证书认证。 我似乎找不到任何使服务器无法通过身份验证的方法。无论客户端证书是受信任的,未知的还是完全不存在的,它都会愉快地为所有旧客户端提供数据。 我对文档的阅读建议,如果不信任客户端,则设置HttpsParameters.setNeedClientAuth(true)应该会导致身份验证失败。我发现有类似问题的人的