Java 如何初始化具有多个信任源的TrustManagerFactory?
我的应用程序有一个个人密钥库,其中包含可在本地网络中使用的可信自签名证书-比如说Java 如何初始化具有多个信任源的TrustManagerFactory?,java,ssl,x509,jsse,Java,Ssl,X509,Jsse,我的应用程序有一个个人密钥库,其中包含可在本地网络中使用的可信自签名证书-比如说mykeystore.jks。我希望能够连接到公共网站(比如google.com)以及使用本地提供的自签名证书的本地网络中的网站 这里的问题是,当我连接到时,路径构建失败,因为设置我自己的密钥库会覆盖包含与JRE绑定的根CA的默认密钥库,并报告异常 sun.security.validator.ValidatorException: PKIX path building failed: sun.security.pr
mykeystore.jks
。我希望能够连接到公共网站(比如google.com)以及使用本地提供的自签名证书的本地网络中的网站
这里的问题是,当我连接到时,路径构建失败,因为设置我自己的密钥库会覆盖包含与JRE绑定的根CA的默认密钥库,并报告异常
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
但是,如果我将CA证书导入我自己的密钥库(mykeystore.jks
),它就可以正常工作。有什么方法可以同时支持这两个方面吗
为此,我有自己的信托经理
public class CustomX509TrustManager implements X509TrustManager {
X509TrustManager defaultTrustManager;
public MyX509TrustManager(KeyStore keystore) {
TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustMgrFactory.init(keystore);
TrustManager trustManagers[] = trustMgrFactory.getTrustManagers();
for (int i = 0; i < trustManagers.length; i++) {
if (trustManagers[i] instanceof X509TrustManager) {
defaultTrustManager = (X509TrustManager) trustManagers[i];
return;
}
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
/* Handle untrusted certificates */
}
}
}
设置插座工厂
HttpsURLConnection.setDefaultSSLSocketFactory(customSSLContext.getSocketFactory());
主节目,
URL targetServer = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection) targetServer.openConnection();
如果我不设置自己的信任管理器,它就可以正常连接。如何获取指向默认密钥存储的“默认信任管理器”?
trustMgrFactory.init(keystore)中的代码>您正在使用自己的个人密钥库而不是系统默认密钥库配置defaultTrustManager
基于读取sun.security.ssl.TrustManagerFactoryImpl的源代码,它看起来像trustMgrFactory.init((KeyStore)null)
将完全满足您的需要(加载系统默认密钥库),基于快速测试,它似乎适合我。我在Commons HttpClient中遇到了同样的问题。我的案例的工作解决方案是通过以下方式为PKIX TrustManager创建委派链:
公共类TrustManagerDelegate实现X509TrustManager{
私人最终X509TrustManager mainTrustManager;
私人最终X509TrustManager trustManager;
私人最终信任策略;
公共信任管理者委派(X509TrustManager主信任管理者、X509TrustManager信任管理者、信任策略信任策略){
this.mainTrustManager=mainTrustManager;
this.trustManager=trustManager;
this.trustStrategy=trustStrategy;
}
@凌驾
公共无效checkClientTrusted(
最终X509Certificate[]链,最终字符串authType)引发CertificateException{
this.trustManager.checkClientTrusted(chain,authType);
}
@凌驾
公共无效检查服务器受信任(
最终X509Certificate[]链,最终字符串authType)引发CertificateException{
如果(!this.trustStrategy.isTrusted(chain,authType)){
试一试{
mainTrustManager.checkServerTrusted(链,authType);
}捕获(证书例外){
this.trustManager.checkServerTrusted(链,authType);
}
}
}
@凌驾
公共X509证书[]getAcceptedIssuers(){
返回此.trustManager.getAcceptedIssuers();
}
}
并以以下方式初始化HttpClient(是的,它很难看):
最终SSLContext SSLContext;
试一试{
sslContext=sslContext.getInstance(“TLS”);
最终的TrustManagerFactory javaDefaultTrustManager=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
javaDefaultTrustManager.init((KeyStore)null);
最终TrustManagerFactory customCaTrustManager=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
init(getKeyStore());
sslContext.init(
无效的
新信托管理人[]{
新TrustManagerDelegate(
(X509TrustManager)customCaTrustManager.GetTrustManager()[0],
(X509TrustManager)javaDefaultTrustManager.GetTrustManager()[0],
新的TrustSelfSignedStrategy()
)
},
安全随机
);
}捕获(最终NoSuchAlgorithmException ex){
抛出新的SSLinitalizationException(例如getMessage(),例如);
}捕获(最终KeyManagementException ex){
抛出新的SSLinitalizationException(例如getMessage(),例如);
}
SSLConnectionSocketFactory sslSocketFactory=新的SSLConnectionSocketFactory(sslContext);
PoolightPClientConnectionManager cm=新的PoolightPClientConnectionManager(
RegistryBuilder.create()文件
.register(“http”,PlainConnectionSocketFactory.getSocketFactory())
.register(“https”,sslSocketFactory)
.build()
);
//最大并行请求数为500
cm.setMaxTotal(500);
cm.setDefaultMaxPerRoute(500);
CredentialsProvider cp=新的基本概念Provider();
cp.setCredentials(
新的AuthScope(apiSettings.getIdcApiUrl(),443),
新用户名密码凭据(apiSettings.getAgencyId(),apiSettings.getAgencyPassword())
);
client=HttpClients.custom()
.setConnectionManager(cm)
.build();
在使用简单HttpsURLConnection的情况下,您可以使用简化版本的委托类:
公共类TrustManagerDelegate实现X509TrustManager{
私人最终X509TrustManager mainTrustManager;
私人最终X509TrustManager trustManager;
公共信任管理者委派(X509TrustManager主信任管理者,X509TrustManager信任管理者){
this.mainTrustManager=mainTrustManager;
this.trustManager=trustManager;
}
@凌驾
公共无效checkClientTrusted(
最终X509Certificate[]链,最终字符串authType)引发CertificateException{
this.trustManager.checkClientTrusted(chain,authType);
}
@凌驾
公共无效检查服务器受信任(
最终X509Certificate[]链,最终字符串authType)引发CertificateException{
试一试{
mainTrustManager.checkServerTrusted(链,authType);
}捕获(证书例外){
this.trustManager.checkServerTrusted(链,authType);
}
}
@凌驾
公共X509证书[]getAcceptedIssuers(){
返回此.trustManager.getAcceptedIssuers();
}
}
答案是我是如何做到的
URL targetServer = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection) targetServer.openConnection();
KeyStore keystore; // Get your own keystore here
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] tm = CompositeX509TrustManager.getTrustManagers(keystore);
sslContext.init(null, tm, null);
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="@raw/extracas"/>
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* Represents an ordered list of {@link X509TrustManager}s with additive trust. If any one of the composed managers
* trusts a certificate chain, then it is trusted by the composite manager.
*
* This is necessary because of the fine-print on {@link SSLContext#init}: Only the first instance of a particular key
* and/or trust manager implementation type in the array is used. (For example, only the first
* javax.net.ssl.X509KeyManager in the array will be used.)
*
* @author codyaray
* @since 4/22/2013
* @see <a href="http://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm">
* http://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm
* </a>
*/
@SuppressWarnings("unused")
public class CompositeX509TrustManager implements X509TrustManager {
private final List<X509TrustManager> trustManagers;
public CompositeX509TrustManager(List<X509TrustManager> trustManagers) {
this.trustManagers = ImmutableList.copyOf(trustManagers);
}
public CompositeX509TrustManager(KeyStore keystore) {
this.trustManagers = ImmutableList.of(getDefaultTrustManager(), getTrustManager(keystore));
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for (X509TrustManager trustManager : trustManagers) {
try {
trustManager.checkClientTrusted(chain, authType);
return; // someone trusts them. success!
} catch (CertificateException e) {
// maybe someone else will trust them
}
}
throw new CertificateException("None of the TrustManagers trust this certificate chain");
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for (X509TrustManager trustManager : trustManagers) {
try {
trustManager.checkServerTrusted(chain, authType);
return; // someone trusts them. success!
} catch (CertificateException e) {
// maybe someone else will trust them
}
}
throw new CertificateException("None of the TrustManagers trust this certificate chain");
}
@Override
public X509Certificate[] getAcceptedIssuers() {
ImmutableList.Builder<X509Certificate> certificates = ImmutableList.builder();
for (X509TrustManager trustManager : trustManagers) {
for (X509Certificate cert : trustManager.getAcceptedIssuers()) {
certificates.add(cert);
}
}
return Iterables.toArray(certificates.build(), X509Certificate.class);
}
public static TrustManager[] getTrustManagers(KeyStore keyStore) {
return new TrustManager[] { new CompositeX509TrustManager(keyStore) };
}
public static X509TrustManager getDefaultTrustManager() {
return getTrustManager(null);
}
public static X509TrustManager getTrustManager(KeyStore keystore) {
return getTrustManager(TrustManagerFactory.getDefaultAlgorithm(), keystore);
}
public static X509TrustManager getTrustManager(String algorithm, KeyStore keystore) {
TrustManagerFactory factory;
try {
factory = TrustManagerFactory.getInstance(algorithm);
factory.init(keystore);
return Iterables.getFirst(Iterables.filter(
Arrays.asList(factory.getTrustManagers()), X509TrustManager.class), null);
} catch (NoSuchAlgorithmException | KeyStoreException e) {
e.printStackTrace();
}
return null;
}
}
SSLFactory sslFactory = SSLFactory.builder()
.withDefaultTrustMaterial() // --> uses the JDK trusted certificates
.withTrustMaterial("/path/to/mykeystore.jks", "password".toCharArray())
.build();
HttpsURLConnection.setDefaultSSLSocketFactory(sslFactory.getSslSocketFactory());