【问题标题】:Validate X.509 certificate against CA in Java在 Java 中针对 CA 验证 X.509 证书
【发布时间】:2011-10-01 13:12:00
【问题描述】:

假设我有这样的东西(客户端代码):

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

        }
    }
};

【问题讨论】:

  • 您好,我正在尝试为checkServerTrusted 实施您的解决方案我有我的.crt 文件,其中包含在打开网站后从浏览器导出的完整证书链。但在for 循环到verify public 我得到了异常。
  • 我的基本问题是pem文件是什么?如何生成它?它包含什么私钥信息或公钥信息。?对不起,我问的问题太基本了..

标签: java ssl x509certificate x509 x509trustmanager


【解决方案1】:

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

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

【讨论】:

  • 这与声称的不符。发送的证书(位于certs[0], 中)从不是 CA 证书。它必须是对等方自己的证书,签名 CA 的证书在链的更上游。此外,这些方法的规范中没有任何内容允许chain 参数为空或零长度,或者允许authTypenull 或空字符串。 authType实际上是一个密钥交换算法的名称,不能是两者之一。
  • LIKE A BOSS:“免责声明:甚至没有尝试编译代码”
【解决方案2】:

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

正如 EJP 所说,不需要实现自己的 TrustManager,您可以使用默认的,并确保已将受信任的 CA 证书添加到您的默认 TrustStore。请参阅this question 了解更多信息。

看看 JDK 中的 CertPathValidator 类,它验证从服务器自己的证书到可信 CA 的连续信任链。有关证书链验证的介绍,请参阅 Oracle's docs

【讨论】:

    【解决方案3】:

    这段代码功能齐全

    这段代码完全功能失调。它完全不安全,甚至不符合它自己的规范。很少需要提供您自己的 TrustManager,默认的信任管理器非常好用。

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

    编辑您的“解决方案”通常不会起作用。它假定链中的每个证书都由您的证书签名。这仅适用于长度为 1 的链,如果签名证书 = 您的证书,则长度为 2。

    【讨论】:

    • 这不是一个真正的答案,也远非正确。如果您编写的软件将通过 ssl 连接到特定服务器,则应覆盖 TrustManager 并主动检查您要连接的服务器是否是您信任的服务器。这可以极大地帮助您抵御 MIM 攻击。
    • 这个答案怎么有 6 票反对?海事组织是完全正确的!覆盖TrustManager 它仅适用于极少数情况下,通常当您在生产模式下进行一些测试时...如果作为 OP 询问他想要针对特定​​ CA 验证证书,则创建一个仅包含此 CA 的信任库而不是覆盖 @ 987654324@.
    • @albciff 我想说很清楚为什么 - 它没有回答问题。
    • @EJP 最初的问题是关于如何针对固定 CA 验证 TLS 证书。您尚未提供有关如何针对固定 CA 验证 TLS 证书的任何信息。
    • 这个答案是正确的——让我感到奇怪和担心的是,这个答案被多次否决。除非您对加密和 Java 非常了解,否则实现您自己的 TrustManager 是一个非常糟糕的主意。特别是,上面 Jakub 提供的具体解决方案并没有像普通的证书链验证那样做任何事情。您应该使用 JDK 的 X509TrustManager。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 1970-01-01
    • 2014-06-09
    • 2011-09-21
    相关资源
    最近更新 更多