如何使用HTTP客户端传递客户端证书?

时间:2013-08-30 21:36:58

标签: apache ssl https ssl-certificate apache-commons-httpclient

我想在服务A和B之间使用相互SSL身份验证。我目前正在实现从Java中的服务A传递客户端证书。我正在使用Apache DefaultHttpClient来执行我的请求。我能够从内部凭据管理器检索我的服务A的客户端证书,并将其保存为字节数组。

DefaultHttpClient client = new DefaultHttpClient();
byte [] certificate = localCertManager.retrieveCert();

我在这方面的经验很少,我很感激你的帮助!

我想也许它应该以某种方式通过HTTP客户端中的参数传递,或者可能在头文件中传递。

如何使用HTTP客户端传递客户端证书?

2 个答案:

答案 0 :(得分:10)

您需要告诉SSLSocketFactory(org.apache.http,而不是javax)关于您的密钥库,并配置您的DefaultHTTPClient以将其用于https连接。

这里有一个例子:http://hc.apache.org/httpcomponents-client-ga/httpclient/examples/org/apache/http/examples/client/ClientCustomSSL.java

答案 1 :(得分:5)

建立连接时客户端证书为sent during the TLS handshake,并且无法在该连接中通过HTTP发送。

沟通是这样分层的:

  • 中的HTTP(应用程序层协议)
  • TLS(表示层协议)
  • 中的TCP(传输层协议)
  • IP(网络层协议)

您需要在TLS握手期间发送客户端证书,然后才能影响HTTP(方法,标头,URL,请求主体)。服务器不接受稍后发送的客户端证书。

我建议从 DefaultHttpClient (已弃用)切换到 CloseableHttpClient ,这样可以更轻松地使用try-with-resources。

Apache HttpClient 4.5使Mutual TLS相当方便。此答案已使用Apache HttpClient 4.5.3进行了测试。

关键的出发点是使用 loadKeyMaterial 将客户端证书及其密钥(客户端密钥对)加载到 SSLContext 中:

SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(
                MutualHttpsMain.class.getResource(TEST_CLIENT_KEYSTORE_RESOURCE),
                password, password,
                (aliases, socket) -> aliases.keySet().iterator().next()
        ).build();

最后使用该套接字工厂构建HTTP客户端:

CloseableHttpClient httpclient = HttpClients
        .custom().setSSLContext(sslContext).build();

使用该客户端,您可以使用隐含的相互TLS身份验证执行所有请求:

CloseableHttpResponse closeableHttpResponse = httpclient.execute(
        new HttpGet(URI.create("https://mutual-tls.example.com/")));

这是Apache HttpClient的完全可运行的TLS示例:

import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;

import javax.net.ssl.SSLContext;
import java.io.Console;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.security.GeneralSecurityException;

public class MutualHttpsMain {
    private static final String TEST_URL = "https://mutual-tls.example.com/";
    private static final String TEST_CLIENT_KEYSTORE_RESOURCE = "/mutual-tls-keystore.p12";

    public static void main(String[] args) throws GeneralSecurityException, IOException {
        Console console = System.console();
        char[] password = console.readPassword("Keystore password: ");
        SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(
                MutualHttpsMain.class.getResource(TEST_CLIENT_KEYSTORE_RESOURCE),
                password, password,
                (aliases, socket) -> aliases.keySet().iterator().next()
        ).build();
        try (CloseableHttpClient httpclient = HttpClients
                .custom().setSSLContext(sslContext).build();
             CloseableHttpResponse closeableHttpResponse = httpclient.execute(
                    new HttpGet(URI.create(TEST_URL)))) {
            console.writer().println(closeableHttpResponse.getStatusLine());
            HttpEntity entity = closeableHttpResponse.getEntity();
            try (InputStream content = entity.getContent();
                 ReadableByteChannel src = Channels.newChannel(content);
                 WritableByteChannel dest = Channels.newChannel(System.out)) {
                ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
                while (src.read(buffer) != -1) {
                    buffer.flip();
                    dest.write(buffer);
                    buffer.compact();
                }
                buffer.flip();
                while (buffer.hasRemaining())
                    dest.write(buffer);
            }
        }
    }
}

使用Gradle或Maven来运行这样的事情通常会更好,但为了尽可能减少这个Yak剃须,我提供了构建和运行它的基线JDK指令。< / p>

从以下页面下载JAR:

将上面的完整示例保存为 MutualHttpsMain.java

将PKCS#12复制到同一目录中的 mutual-tls-keystore.p12

按如下方式编译(在macOS / Linux / * nix-likes上):

javac MutualHttpsMain.java -cp httpclient-4.5.3.jar:httpcore-4.4.8.jar

或在Windows上:

javac MutualHttpsMain.java -cp httpclient-4.5.3.jar;httpcore-4.4.8.jar

运行如下(在macOS / Linux / * nix-likes上):

java -cp httpclient-4.5.3.jar:commons-codec-1.10.jar:commons-logging-1.2.jar:httpcore-4.4.8.jar:. MutualHttpsMain

运行如下(在Windows上):

java -cp httpclient-4.5.3.jar;commons-codec-1.10.jar;commons-logging-1.2.jar;httpcore-4.4.8.jar;. MutualHttpsMain