Apache HttpClient 4.3 和 x509 客户端证书进行身份验证
Posted
技术标签:
【中文标题】Apache HttpClient 4.3 和 x509 客户端证书进行身份验证【英文标题】:Apache HttpClient 4.3 and x509 client certificate to authenticate 【发布时间】:2014-05-01 15:26:01 【问题描述】:现在我正在寻找有关如何通过 HttpComponentsMessageSender(不相关)重写客户端 x509 证书身份验证的已弃用解决方案的任务的解决方案。
例如,不推荐使用的解决方案是:
SSLSocketFactory lSchemeSocketFactory = new SSLSocketFactory(this.keyStore, this.keyStorePassword);
Scheme sch = new Scheme("https", 443, lSchemeSocketFactory);
DefaultHttpClient httpClient = (DefaultHttpClient)getHttpClient();
httpClient.getConnectionManager().getSchemeRegistry().register(sch);
作为我正在使用的 CloseableHttpClient 的新解决方案:
SSLContextBuilder sslContextBuilder = SSLContexts.custom()
// this key store must contain the key/cert of the client
.loadKeyMaterial(keyStore, keyStorePassword.toCharArray());
if (trustStore != null)
// this key store must contain the certs needed and trusted to verify the servers cert
sslContextBuilder.loadTrustMaterial(trustStore);
SSLContext sslContext = sslContextBuilder.build();
LayeredConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
// Create a registry of custom connection socket factories for supported
// protocol schemes / https
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslsf)
.register("http", new PlainConnectionSocketFactory())
.build();
PoolingHttpClientConnectionManager connPoolControl =
new PoolingHttpClientConnectionManager(socketFactoryRegistry);
setConnPoolControl(connPoolControl);
getClientBuilder().setSSLSocketFactory(sslsf);
我仍然收到来自服务器的 403 禁止。但是,当我使用“已弃用”版本的解决方案时,效果很好。 SSL 证书已签署 Thawte。
有什么想法吗? 谢谢
【问题讨论】:
【参考方案1】:Tomas,也许为时已晚,但我希望它能帮助其他人...... 有一种方法,我用它来使用 Apache HttpClient 4.3 创建 CloseableHttpClient:
public static CloseableHttpClient prepareClient()
try
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).useTLS().build();
HttpClientBuilder builder = HttpClientBuilder.create();
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
builder.setSSLSocketFactory(sslConnectionFactory);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslConnectionFactory)
.register("http", new PlainConnectionSocketFactory())
.build();
HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
builder.setConnectionManager(ccm);
return builder.build();
catch (Exception ex)
return null;
Apache 基金会从 4.4 版本开始将 org.apache.http.conn.ssl.SSLContextBuilder、org.apache.http.conn.ssl.SSLContexts 和 org.apache.http.conn.ssl.SSLSocketFactory 移至弃用,@987654321 @您可以找到 Apache Client 4.5.2 API 弃用列表。因此,可以像这样更改 pervious 方法:
public static CloseableHttpClient prepareClient()
try
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
HttpClientBuilder builder = HttpClientBuilder.create();
SSLConnectionSocketFactory sslConnectionFactory =
new SSLConnectionSocketFactory(sslContext.getSocketFactory(),
new NoopHostnameVerifier());
builder.setSSLSocketFactory(sslConnectionFactory);
Registry<ConnectionSocketFactory> registry =
RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslConnectionFactory)
.register("http", new PlainConnectionSocketFactory())
.build();
HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
builder.setConnectionManager(ccm);
return builder.build();
catch (Exception ex)
LOG.error("couldn't create httpClient!! ", ex.getMessage(), ex);
return null;
NoopHostnameVerifier
NO_OP HostnameVerifier 本质上是主机名验证 离开。此实现是无操作的,并且从不抛出 SSL 异常。
如果你需要验证主机名,你可以使用 DefaultHostnameVerifier 或者你可以实现你的自定义主机名验证器。
【讨论】:
【参考方案2】:您需要创建一个包含受信任 CA 的密钥库,即trust.jks
。在此密钥库中,您应该只放置应用程序将要连接的服务器的证书。
然后,您需要一个用于服务器身份的密钥库,即identity.jks
。在此密钥库中,您应该将私钥 + 证书 + CA 链存储在一个别名(名称)下,您的应用程序将使用该别名向服务器进行身份验证。
然后你可以像这样构建HttpClient
:
public static HttpClient getHttpClient() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, KeyManagementException
KeyStore identityKeyStore = KeyStore.getInstance("jks");
identityKeyStore.load(MyClass.class.getClassLoader().getResourceAsStream("identity.jks"), "identity_password".toCharArray());
KeyStore trustKeyStore = KeyStore.getInstance("jks");
trustKeyStore.load(MyClass.class.getClassLoader().getResourceAsStream("trust.jks"), "trust_password".toCharArray());
SSLContext sslContext = SSLContexts
.custom()
// load identity keystore
.loadKeyMaterial(identityKeyStore, "identity_password".toCharArray(), new PrivateKeyStrategy()
@Override
public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket)
return "identity_alias";
)
// load trust keystore
.loadTrustMaterial(trustKeyStore, null)
.build();
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
new String[]"TLSv1.2", "TLSv1.1",
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
return HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.build();
要构建identity.jks
,需要CAs链、公钥和私钥:
$1 = mycustomidentity
# make the keycert bundle for pkcs12 keystore
cat intermediate/certs/ca-chain.cert.pem \
intermediate/certs/$1.cert.pem \
intermediate/private/$1.key.pem \
> intermediate/keycerts/$1.full-chain.keycert.pem
# generate the pkcs12 keystore with the alias of the server url
openssl pkcs12 -export \
-in intermediate/keycerts/$1.full-chain.keycert.pem \
-out intermediate/pkcs12s/$1.full-chain.p12 \
-name $1 \
-noiter -nomaciter
# .p12 to .jks
keytool -importkeystore -srckeystore $1.full-chain.p12 \
-srcstoretype pkcs12 -srcalias $1 \
-destkeystore identity.jks -deststoretype jks \
-deststorepass identity_password -destalias identity_alias
trust.jks
文件只需要服务器的证书(见https://***.com/a/36427118/2692914或https://***.com/a/7886248/2692914),改别名没有问题:
# .crt, .cer into a .jks
keytool -import -alias trust_alias -file server_certificate.crt \
-keystore trust.jks
【讨论】:
这里的 DnieUtils 是什么? 类的名称。在这种情况下获取将加载资源的类加载器【参考方案3】:以下是 HttpClient 4.4+ 的代码(更新了 @Daniyar 4.4+ 的代码)
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
public static CloseableHttpClient createApacheHttp4ClientWithClientCertAuth()
try
SSLContext sslContext = SSLContexts
.custom()
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
.build();
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslContext,
new DefaultHostnameVerifier());
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("https", sslConnectionFactory)
.register("http", new PlainConnectionSocketFactory())
.build();
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setSSLSocketFactory(sslConnectionFactory);
builder.setConnectionManager(new PoolingHttpClientConnectionManager(registry));
return builder.build();
catch (Exception ex)
return null;
【讨论】:
以上是关于Apache HttpClient 4.3 和 x509 客户端证书进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章
Apache HttpClient 4.3 和 x509 客户端证书进行身份验证
如何使用 Apache HttpClient 4.3 处理 Cookie
忽略 Apache HttpClient 4.3 中的 SSL 证书