Подтвердить сертификат X.509 против CA в Java

Предположим, что у меня есть что-то вроде этого (код на стороне клиента):

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, которому я должен проверять (только против этого).

Каждый ответ оценивается.

EDIT:

В ответ на 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);
                }
            }

        }
    }
};

Ответы

Ответ 1

Я предполагаю, что самозаверяющий сертификат вашего ЦС уже загружен следующим образом:

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

Отказ от ответственности: Даже не пытались скомпилировать код

Ответ 2

Принятый ответ крайне неверен. Он не криптографически не проверяет соединение между сертификатом сервера и доверенным центром сертификации. В общем, вам почти не нужно будет внедрять собственный TrustManager, поэтому очень опасно.

Как указано в EJP, нет необходимости реализовывать собственный TrustManager, вы можете просто использовать стандартную версию и убедиться, что доверенный сертификат CA был добавлен в ваш TrustStore по умолчанию. См. этот вопрос для получения дополнительной информации.

Взгляните на класс CertPathValidator из JDK, который проверяет непрерывную цепочку доверия от собственного сертификата сервера до доверенный ЦС. См. Oracle docs для ознакомления с проверкой цепочки сертификатов.

Ответ 3

Этот код полностью функциональный

Этот код полностью дисфункциональный. Он абсолютно небезопасен, а также не соответствует его собственным спецификациям. Существует редко необходимость поставлять собственный TrustManager, по умолчанию он работает очень хорошо.

Все, что вам нужно сделать, это убедиться, что сертификат ЦС, который у вас есть, присутствует в вашем доверенном магазине, а затем установить системное свойство javax.net.ssl.trustStore, чтобы указать на него, если он не является файлом доверия доверия по умолчанию. Вам не нужно писать код вообще, кроме, возможно, System.setProperty(), если вы не установите его с помощью командной строки -D.

РЕДАКТИРОВАТЬ Ваше "решение", конечно, не будет работать вообще. Он предполагает, что каждый сертификат в цепочке подписывается вашим сертификатом. Это может быть справедливо только для цепей длиной 1 или длины 2, если сертификат подписи = ваш сертификат.