Проверьте сертификат X.509 против CA в Java - PullRequest
15 голосов
/ 08 июля 2011

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

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.

Все сертификаты подписаны моим самозаверяющим ЦС, и именно этот ЦС я должен проверить (только против этого).

Каждый ответ приветствуется.

РЕДАКТИРОВАТЬ:

В ответ на 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 ]

22 голосов
/ 09 июля 2011

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

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

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

4 голосов
/ 21 октября 2017

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

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

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

0 голосов
/ 10 июля 2011

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

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

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

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

...