在 Java 中根据 CA 验证 X.509 证书
可以说我有这样的东西(客户端代码):
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);
该代码功能齐全,但我真的不知道如何根据 pem 文件中可用的一个具体 CA 证书来验证服务器的证书。
所有证书均由我的自签名 CA 签名,这是我需要验证的 CA(仅针对此证书)。
每个答案都值得赞赏。
编辑:
回应 jglouie (非常感谢你这样 - 无法投票赞成你的答案)。
我找到了解决方案:
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);
}
}
}
}
};
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.
All certificates are signed by my self-signed CA, and it is the CA I need to validate against (only against this one).
Every answer is appreciated.
EDIT:
In response to jglouie (thank you very much this way - can not vote up your answer).
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);
}
}
}
}
};
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我假设您的CA的自签名证书已经加载,如下所示:
然后在检查证书的方法中:
免责声明:甚至没有尝试编译代码
I assume that the self-signed certificate of your CA is already loaded as follows:
Then in the method to check certificate:
Disclaimer: Have not even atempted to compile the code
接受的答案是极其不正确的。它不会以加密方式验证服务器证书和受信任的证书颁发机构之间的任何连接。一般来说,您几乎不需要实现自己的 TrustManager,这样做是极其危险的。
正如 EJP 所说,无需实现自己的 TrustManager,您只需使用默认的 TrustManager 即可,并确保受信任的 CA 证书已添加到您的默认 TrustStore 中。有关详细信息,请参阅此问题。
查看 CertPathValidator 类来自 JDK,它验证从服务器自己的证书到受信任的 CA 的连续信任链。请参阅 Oracle 文档了解证书链验证。
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.
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 question for more information.
Take a look at the CertPathValidator class from the JDK, which verifies a continuous chain of trust from the server's own certificate up through a trusted CA. See Oracle's docs for an introduction to certificate chain validation.
。该代码完全功能失调。它完全不安全,甚至不符合其自身的规范。很少需要提供自己的 TrustManager,默认的 TrustManager 效果很好。
您需要做的就是确保您拥有的 CA 证书存在于您的信任库中,然后将系统属性 javax.net.ssl.trustStore 设置为指向它(如果它不是默认值) Java 信任库文件。如果您不通过命令行 -D 选项进行设置,则除了
System.setProperty()
之外,您根本不需要编写任何代码。编辑你的“解决方案”一般来说肯定行不通。它假设链中的每个证书都由您的证书签名。这仅适用于长度为 1 的链,或者如果签名证书 = 您的证书,则长度为 2。
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.
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 possiblySystem.setProperty()
, if you don't set it via the command line -D option.EDIT Your '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.