Java 实现 X509TrustManager - 将部分验证传递给现有验证器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19005318/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Implementing X509TrustManager - passing on part of the verification to existing verifier
提问by user93353
I need to ignore the PKIX path building exception
我需要忽略 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
I know how to do this by writing my own class implementing X509TrustManager
where I always return true
from isServerTrusted
.
我知道如何通过编写我自己的类来实现X509TrustManager
我总是return true
来自的地方isServerTrusted
。
However, I don't want to trust all servers & all clients.
但是,我不想信任所有服务器和所有客户端。
- I want all the default verification to be done for clients as is done currently.
- For servers, I want to ignore server cert verification only for one particular cert but want to go ahead and verify it as is done currently (for eg. using cacerts store).
- 我希望像当前那样为客户完成所有默认验证。
- 对于服务器,我只想忽略一个特定证书的服务器证书验证,但想继续并按照当前的方式进行验证(例如,使用 cacerts 存储)。
How can I achieve something like this - i.e. pass on part of the verification to whatever was the X509TrustFactory object before I replaced it.
我怎样才能实现这样的事情 - 即在我替换它之前将部分验证传递给 X509TrustFactory 对象。
i.e. this is what I want to do
即这就是我想要做的
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
}
Also I don't want to disturb the existing isClientTrusted
verification.
我也不想打扰现有的isClientTrusted
验证。
How can I do this?
我怎样才能做到这一点?
回答by Bruno
You can get hold of the existing default trust manager and wrap it in your own using something like this:
您可以使用以下内容获取现有的默认信任管理器并将其包装在您自己的中:
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);
You can then implement your own logic around finalTm.checkServerTrusted(chain, authType);
.
然后,您可以围绕 实现您自己的逻辑finalTm.checkServerTrusted(chain, authType);
。
However, you should make sure you're making an exception for the specific certificate you want to ignore.
但是,您应该确保对要忽略的特定证书进行例外处理。
What you're doing in the following is letting through anycertificate with these Issuer DN and Subject DN (which isn't difficult to forge):
您在下面所做的是让具有这些颁发者 DN 和主题 DN(这并不难伪造)的任何证书通过:
if(chain[0].getIssuerDN().getName().equals("MyTrustedServer") && chain[0].getSubjectDN().getName().equals("MyTrustedServer"))
return true;
You could instead load the X509Certificate
instance from a known reference and compare the actual value in the chain.
您可以改为X509Certificate
从已知引用加载实例并比较链中的实际值。
In addition, checkClientTrusted
and checkServerTrusted
are not methods that return true
or false
, but void
methods that will succeed silently by default. If there's something wrong with the certificate you expect, throw a CertificateException
explicitly.
此外,checkClientTrusted
andcheckServerTrusted
不是返回true
or 的方法false
,而是void
默认情况下会静默成功的方法。如果您期望的证书有问题,请CertificateException
明确抛出。
回答by Johannes Brodwall
Instead of implementing X509TrustManager
to trust anycertificate, you can create a trust manager from the specific certificate in question. Load the certificate from a .p12
or .jks
keystore or from a .crt
-file (you can copy a certificate from the browser into a file, in Chrome by clicking the padlock and selecting Certificate). The code is shorter than implementing your own X509TrustManager
:
您可以从相关的特定证书创建信任管理器,而不是实施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();
}
You can use it by calling HttpsURLConnection.setSSLSocketFactory(createSSLSocketFactory(crtFile))
(you probably want to initialize the socket factory once and reuse it, though).
您可以通过调用来使用它HttpsURLConnection.setSSLSocketFactory(createSSLSocketFactory(crtFile))
(不过,您可能希望初始化套接字工厂一次并重用它)。