在 Java 中根据 CA 验证 X.509 证书

发布于 2024-11-19 13:12:22 字数 2476 浏览 5 评论 0原文

可以说我有这样的东西(客户端代码):

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

北斗星光 2024-11-26 13:12: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);
      }  
}

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

I assume that the self-signed certificate of your CA is already loaded as follows:

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

琉璃梦幻 2024-11-26 13:12:22

接受的答案是极其不正确的。它不会以加密方式验证服务器证书和受信任的证书颁发机构之间的任何连接。一般来说,您几乎不需要实现自己的 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.

叶落知秋 2024-11-26 13:12:22

此代码功能齐全

。该代码完全功能失调。它完全不安全,甚至不符合其自身的规范。很少需要提供自己的 TrustManager,默认的 TrustManager 效果很好。

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

编辑你的“解决方案”一般来说肯定行不通。它假设链中的每个证书都由您的证书签名。这仅适用于长度为 1 的链,或者如果签名证书 = 您的证书,则长度为 2。

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.

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文