实施 X509TrustManager - 将部分验证传递给现有验证者
Posted
技术标签:
【中文标题】实施 X509TrustManager - 将部分验证传递给现有验证者【英文标题】:Implementing X509TrustManager - passing on part of the verification to existing verifier 【发布时间】:2013-10-01 01:38:50 【问题描述】:我需要忽略 PKIX 路径构建异常
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderExc
ption: unable to find valid certification path to requested target
我知道如何通过编写自己的实现 X509TrustManager
的类来做到这一点,而我总是从 isServerTrusted
实现 return true
。
但是,我不想信任所有服务器和所有客户端。
我希望像当前一样为客户完成所有默认验证。 对于服务器,我只想忽略一个特定证书的服务器证书验证,但想继续验证它,就像目前所做的那样(例如,使用 cacerts 存储)。我怎样才能实现这样的事情 - 即在我替换之前将部分验证传递给 X509TrustFactory 对象。
即这就是我想做的事情
public boolean isServerTrusted(X509Certificate[] chain)
if(chain[0].getIssuerDN().getName().equals("MyTrustedServer") && chain[0].getSubjectDN().getName().equals("MyTrustedServer"))
return true;
// else I want to do whatever verification is normally done
另外我不想打扰现有的isClientTrusted
验证。
我该怎么做?
【问题讨论】:
【参考方案1】:您可以获取现有的默认信任管理器,并使用以下方式将其包装在您自己的中:
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Using null here initialises the TMF with the default trust store.
tmf.init((KeyStore) null);
// Get hold of the default trust manager
X509TrustManager x509Tm = null;
for (TrustManager tm : tmf.getTrustManagers())
if (tm instanceof X509TrustManager)
x509Tm = (X509TrustManager) tm;
break;
// Wrap it in your own class.
final X509TrustManager finalTm = x509Tm;
X509TrustManager customTm = new X509TrustManager()
@Override
public X509Certificate[] getAcceptedIssuers()
return finalTm.getAcceptedIssuers();
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException
finalTm.checkServerTrusted(chain, authType);
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException
finalTm.checkClientTrusted(chain, authType);
;
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] customTm , null);
// You don't have to set this as the default context,
// it depends on the library you're using.
SSLContext.setDefault(sslContext);
然后您可以围绕finalTm.checkServerTrusted(chain, authType);
实现您自己的逻辑。
但是,您应该确保为要忽略的特定证书设置例外。
您在下面所做的是让具有这些颁发者 DN 和主题 DN(不难伪造)的任何证书通过:
if(chain[0].getIssuerDN().getName().equals("MyTrustedServer") && chain[0].getSubjectDN().getName().equals("MyTrustedServer"))
return true;
您可以改为从已知引用加载 X509Certificate
实例并比较链中的实际值。
此外,checkClientTrusted
和 checkServerTrusted
不是返回 true
或 false
的方法,而是默认情况下会静默成功的 void
方法。如果您期望的证书有问题,请明确抛出CertificateException
。
【讨论】:
+1 非常感谢您的评论 "// 在此处使用 null 会使用默认信任库初始化 TMF。"。如果这在 api 文档中会很好;) 您能否解释一下并给我看这部分的示例:您可以改为从已知引用加载 X509Certificate 实例并比较链中的实际值。跨度> @kaze,如果你想比较 exact 证书,你可以从任何你想要的来源加载 X.509 证书(例如,通过 CertificateFactory 的 PEM 文件的密钥库),然后您可以将链(元素 0)中呈现的内容与参考实例进行比较。不确定equals
比较什么,但您当然可以比较getEncoded()
在两个X509Certificate
实例上的结果(当然是字节数组比较,而不是引用)。
@Dori 说真的。仅凭这一点,这篇文章就值得一票。
@OnePunchMan 您可以将PEM
编码的证书存储在您的类路径中,并比较X509Certificate
实例本身。一旦攻击者无法使用假证书篡改您的 jar,您就可以完全控制您的信任库【参考方案2】:
您可以从相关的特定证书创建信任管理器,而不是实施 X509TrustManager
来信任 任何 证书。从.p12
或.jks
密钥库或.crt
文件加载证书(您可以在Chrome 中单击挂锁并选择证书,将证书从浏览器复制到文件中)。代码比实现自己的X509TrustManager
要短:
private static SSLSocketFactory createSSLSocketFactory(File crtFile) throws GeneralSecurityException, IOException
SSLContext sslContext = SSLContext.getInstance("SSL");
// Create a new trust store, use getDefaultType for .jks files or "pkcs12" for .p12 files
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
// You can supply a FileInputStream to a .jks or .p12 file and the keystore password as an alternative to loading the crt file
trustStore.load(null, null);
// Read the certificate from disk
X509Certificate result;
try (InputStream input = new FileInputStream(crtFile))
result = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(input);
// Add it to the trust store
trustStore.setCertificateEntry(crtFile.getName(), result);
// Convert the trust store to trust managers
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
sslContext.init(null, trustManagers, null);
return sslContext.getSocketFactory();
您可以通过调用 HttpsURLConnection.setSSLSocketFactory(createSSLSocketFactory(crtFile))
来使用它(不过,您可能希望初始化一次套接字工厂并重用它)。
【讨论】:
以上是关于实施 X509TrustManager - 将部分验证传递给现有验证者的主要内容,如果未能解决你的问题,请参考以下文章
java HttpsURLConnection怎么绕过证书,原理是啥