假设我有这样的东西(客户端代码):
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);
这段代码是完整的功能,但我真的不能弄清楚,如何验证服务器的证书对一个具体的CA证书,我有在pem文件中可用。
所有证书都是由我的自签名CA签名的,我需要验证的就是这个CA(只针对这个CA)。
每个答案都是赞赏的。
编辑:作为对jglouie的回应(非常感谢你这样做-不能投票给你的答案)。
I found 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);
}
}
}
}
};
我假设您的CA的自签名证书已经加载如下:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream finStream = new FileInputStream("CACertificate.pem");
X509Certificate caCertificate = (X509Certificate)cf.generateCertificate(finStream);
然后在检查证书的方法中:
@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);
}
}
免责声明:甚至没有尝试编译代码
接受的答案是非常错误的。它不会对服务器证书和受信任的证书颁发机构之间的任何连接进行加密验证。一般来说,您几乎不需要实现自己的TrustManager,这样做是非常危险的。
正如ejb所述,不需要实现您自己的TrustManager,您只需使用默认的TrustManager,并确保受信任的CA证书已添加到您的默认TrustStore中。有关更多信息,请参阅此问题。
看一下JDK中的CertPathValidator类,它验证从服务器自己的证书到受信任的CA的连续信任链。参见Oracle的文档了解证书链验证。
这段代码是完全可用的
这段代码完全不正常。它是完全不安全的,甚至不符合它自己的规范。很少需要提供自己的TrustManager,默认的TrustManager工作得很好。
您所需要做的就是确保您拥有的CA证书存在于您的信任库中,然后将系统属性javax.net.ssl.trustStore
设置为在它不是默认Java信任库文件时指向它。如果不通过命令行-D选项设置System.setProperty()
,则不需要编写任何代码。
EDIT你的"解决方案"肯定不会在一般情况下工作。它假设链中的每个证书都由您的证书签名。只有当签名证书=您的证书时,长度为1或长度为2的链才能成立。