在 Java 中针对 CA 验证 X.509 证书
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6629473/
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
Validate X.509 certificate against CA in Java
提问by Jakub Trune?ek
Lets say I have something like this (client side code):
假设我有这样的东西(客户端代码):
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(null, trustAllCerts, null);
SocketFactory sf = sslc.getSocketFactory();
SSLSocket s = (SSLSocket) sf.createSocket("127.0.0.1", 9124);
This code is complete functional, but I really can not figure out, how to validate server's certificate against one concrete CA certificate that I have available in pem file.
这段代码是完整的功能,但我真的不知道如何根据我在 pem 文件中可用的一个具体 CA 证书来验证服务器的证书。
All certificates are signed by my self-signed CA, and it is the CA I need to validate against (only against this one).
所有证书都由我的自签名 CA 签名,这是我需要验证的 CA(仅针对此 CA)。
Every answer is appreciated.
每个答案都值得赞赏。
EDIT:
编辑:
In response to jglouie(thank you very much this way - can not vote up your answer).
回应jglouie(非常感谢你 - 不能投票给你的答案)。
I founded the solution:
我建立了解决方案:
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws CertificateException {
InputStream inStream = null;
try {
// Loading the CA cert
URL u = getClass().getResource("tcp/cacert.pem");
inStream = new FileInputStream(u.getFile());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate ca = (X509Certificate) cf.generateCertificate(inStream);
inStream.close();
for (X509Certificate cert : certs) {
// Verifing by public key
cert.verify(ca.getPublicKey());
}
} catch (Exception ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
inStream.close();
} catch (IOException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
};
采纳答案by Cratylus
I assume that the self-signed certificate of your CA is already loaded as follows:
我假设您的 CA 的自签名证书已经加载如下:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream finStream = new FileInputStream("CACertificate.pem");
X509Certificate caCertificate = (X509Certificate)cf.generateCertificate(finStream);
Then in the method to check certificate:
然后在检查证书的方法中:
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws CertificateException {
if (certs == null || certs.length == 0) {
throw new IllegalArgumentException("null or zero-length certificate chain");
}
if (authType == null || authType.length() == 0) {
throw new IllegalArgumentException("null or zero-length authentication type");
}
//Check if certificate send is your CA's
if(!certs[0].equals(caCertificate)){
try
{ //Not your CA's. Check if it has been signed by your CA
certs[0].verify(caCertificate.getPublicKey())
}
catch(Exception e){
throw new CertificateException("Certificate not trusted",e);
}
}
//If we end here certificate is trusted. Check if it has expired.
try{
certs[0].checkValidity();
}
catch(Exception e){
throw new CertificateException("Certificate not trusted. It has expired",e);
}
}
Disclaimer:Have not even atempted to compile the code
免责声明:甚至没有尝试编译代码
回答by user207421
This code is completely functional
此代码功能齐全
This code is completely dysfunctional.It is completely insecure, as well as not even conforming to its own specification. There is rarely a need to supply your own TrustManager, the default one works really well.
这段代码完全不正常。它是完全不安全的,甚至不符合自己的规范。很少需要提供您自己的 TrustManager,默认的一个非常好用。
All you need to do is ensure that the CA certificate you have is present in your truststore, and then set the system property javax.net.ssl.trustStore
to point to it if it isn't the default Java truststore file. You don't need to write any code at all beyond possibly System.setProperty()
, if you don't set it via the command line -D option.
您需要做的就是确保您拥有的 CA 证书存在于您的信任库中,然后javax.net.ssl.trustStore
如果它不是默认的 Java 信任库文件,则将系统属性设置为指向它。System.setProperty()
如果您不通过命令行 -D 选项设置它,则您根本不需要编写任何超出可能的代码。
EDITYour 'solution' certainly won't work in general. It assumes that every certificate in the chain is signed by your certificate. That can only be true for chains of length 1, or length 2 if the signing certificate = your certificate.
编辑您的“解决方案”肯定不会在一般情况下起作用。它假定链中的每个证书都由您的证书签名。这仅适用于长度为 1 或长度为 2 的链,如果签名证书 = 您的证书。
回答by gsteff
The accepted answer is extremely incorrect. It doesn't cryptographically verify any connection between the server certificate and the trusted certificate authority. In general, you should almost never need to implement your own TrustManager, doing so is extremely dangerous.
接受的答案是非常不正确的。它不会以加密方式验证服务器证书和受信任的证书颁发机构之间的任何连接。一般来说,您几乎不需要实现自己的 TrustManager,这样做是非常危险的。
As EJP stated, there's no need to implement your own TrustManager, you can just use the default one, and ensure that the trusted CA certificate has been added to your default TrustStore. See this questionfor more information.
正如 EJP 所说,不需要实现自己的 TrustManager,您可以使用默认的,并确保受信任的 CA 证书已添加到您的默认 TrustStore。有关更多信息,请参阅此问题。
Take a look at the CertPathValidatorclass from the JDK, which verifies a continuous chain of trust from the server's own certificate up through a trusted CA. See Oracle's docsfor an introduction to certificate chain validation.
看一看来自 JDK的CertPathValidator类,它验证从服务器自己的证书到受信任的 CA 的连续信任链。有关证书链验证的介绍,请参阅Oracle 的文档。