在Java中针对CA验证X.509证书

时间:2011-07-08 19:30:32

标签: java ssl x509certificate x509 x509trustmanager

让我说我有类似的东西(客户端代码):

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);
                }
            }

        }
    }
};

3 个答案:

答案 0 :(得分:22)

我假设您的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);
      }  
}

免责声明:甚至没有尝试编译代码

答案 1 :(得分:4)

接受的答案非常不正确。它不会以加密方式验证服务器证书与可信证书颁发机构之间的任何连接。通常,您几乎不需要实现自己的TrustManager,这样做非常危险。

正如EJP所说,没有必要实现自己的TrustManager,您可以使用默认的TrustManager,并确保已将可信CA证书添加到您的默认TrustStore。有关详细信息,请参阅this question

查看JDK中的CertPathValidator类,它通过受信任的CA验证来自服务器自身证书的连续信任链。有关证书链验证的介绍,请参阅Oracle's docs

答案 2 :(得分:-5)

  

此代码功能齐全

此代码完全功能失调。它完全不安全,甚至不符合自己的规范。很少需要提供您自己的TrustManager,默认的工作正常。

您需要做的就是确保您拥有的CA证书存在于您的信任库中,然后将系统属性javax.net.ssl.trustStore设置为指向它,如果它不是默认的Java信任库文件。如果不通过命令行-D选项设置代码,则根本不需要编写除System.setProperty()以外的任何代码。

编辑您的“解决方案”肯定不会起作用。它假定链中的每个证书都由您的证书签名。只有当签名证书=您的证书时,长度为1或长度为2的链才能成立。