2015-05-30 6 views
2

Я использую Java для разработки простого SSL-сервера/клиента. Пожалуйста, игнорируйте trustStore, который не используется. Когда сервер устанавливает serverSock.setNeedClientAuth (false), он работает нормально. Однако, когда serverSock.setNeedClientAuth (истина) устанавливается, ошибка подсказывает, чтоОшибка SSL-связи Java SSL

*** ServerHello, TLSv1 
.... 
*** 


    *** ECDH ServerKeyExchange 
    Server key: Sun EC public key, 256 bits 
     public x coord: 61670393751189389356366022463080915345182339021857366784148461923453434926203 
     public y coord: 11927389709535675731950695034443898307097761611191306989959806723983291216258 
     parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7) 
    **main, handling exception: java.lang.NullPointerException 
    %% Invalidated: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]** 
    main, SEND TLSv1 ALERT: fatal, description = internal_error 
    main, WRITE: TLSv1 Alert, length = 2 
    main, called closeSocket() 
Exception in thread "main" javax.net.ssl.SSLException: java.lang.NullPointerException 
    at sun.security.ssl.Alerts.getSSLException(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.handleException(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.handleException(Unknown Source) 
    at sun.security.ssl.AppInputStream.read(Unknown Source) 
    at java.io.InputStream.read(Unknown Source) 
    at cn.secure.CAServer.start(SecureServer.java:100) 
    at cn.secure.SecureServer.main(SecureServer.java:33) 
Caused by: java.lang.NullPointerException 
    at sun.security.ssl.HandshakeMessage$CertificateRequest.<init>(Unknown Source) 
    at sun.security.ssl.ServerHandshaker.clientHello(Unknown Source) 
    at sun.security.ssl.ServerHandshaker.processMessage(Unknown Source) 
    at sun.security.ssl.Handshaker.processLoop(Unknown Source) 
    at sun.security.ssl.Handshaker.process_record(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source) 
    ... 4 more 

кажется ServerHello не делается.

Следующий мой код. Пожалуйста, дайте мне знать, как его решить.

// Server code 
class CAServer 
{ 
    private SSLContext ctx; 
    private KeyManagerFactory kmf; 
    private TrustManagerFactory tmf; 
    private SSLServerSocket serverSock; 

    public void init() throws NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, KeyManagementException 
    { 
     ctx=SSLContext.getInstance("TLS"); 
     kmf=KeyManagerFactory.getInstance("SunX509"); 
     tmf=TrustManagerFactory.getInstance("SunX509"); 
     char[] pwd="111".toCharArray(); 

     KeyStore ks=KeyStore.getInstance("JKS"); 
     KeyStore ts=KeyStore.getInstance("JKS"); 

     ks.load(new FileInputStream("C:/Users/Jim/ca.keystore"), pwd);   
     ts.load(new FileInputStream("C:/Users/Jim/ca.keystore"), pwd); // unused  

     kmf.init(ks,pwd); 
     tmf.init(ts); 

     TrustManager[] trustClientCerts = new TrustManager[] { new X509TrustManager() { 

      @Override 
      public X509Certificate[] getAcceptedIssuers() { 
       return null; 
      } 

      @Override 
      public void checkClientTrusted(X509Certificate[] certs,String authType) { 

      } 

      @Override 
      public void checkServerTrusted(X509Certificate[] certs,String authType) { 
      } 
     } 
     }; 


     ctx.init(kmf.getKeyManagers(),trustClientCerts, null); 

     //init server 
     serverSock=(SSLServerSocket)ctx.getServerSocketFactory().createServerSocket(13000); 

     serverSock.setNeedClientAuth(true); 
    } 

    public void start() throws IOException 
    { 
     System.out.println("My Secure server start"); 
     while(true) 
     { 
      Socket s=serverSock.accept(); 

      InputStream input=s.getInputStream(); 

      byte[] c=new byte[256]; 
      input.read(c);   **// error(NullPointer) occurs here** 
      System.out.println(new String(c)); 
     } 
    } 

} 


// Client code 
class MyClient 
{ 
    private SSLContext ctx; 
    KeyManagerFactory kmf; 
    TrustManagerFactory tmf; 
    private SSLSocket clientSock; 

    public void init() throws NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException, KeyManagementException, UnrecoverableKeyException 
    { 
     ctx=SSLContext.getInstance("TLS"); 
     kmf=KeyManagerFactory.getInstance("SunX509"); 
     tmf=TrustManagerFactory.getInstance("SunX509"); 

     char[] pwd="111".toCharArray(); 

     KeyStore ks=KeyStore.getInstance("JKS"); 
     KeyStore ts=KeyStore.getInstance("JKS"); 

     ks.load(new FileInputStream("C:/Users/jim/alice.keystore"), pwd);   
     ts.load(new FileInputStream("C:/Users/jim/alice.keystore"), pwd); //unused 

     TrustManager[] trustServerCerts = new TrustManager[]{new X509TrustManager() 
     { 

      @Override 
      public X509Certificate[] getAcceptedIssuers() { 
       return null; 
      } 

      @Override 
      public void checkClientTrusted(X509Certificate[] certs, String authType) { 
      } 

      @Override 
      public void checkServerTrusted(X509Certificate[] certs, String authType) 
      { 

       for(X509Certificate c :certs){ 
        System.out.println(c.getSubjectDN().getName()); 
       } 

      } 
      } 
     }; 

     kmf.init(ks, pwd); 
     tmf.init(ts); 

     ctx.init(kmf.getKeyManagers(), trustServerCerts, null); 

     clientSock=(SSLSocket)ctx.getSocketFactory().createSocket("127.0.0.1", 13000); 
     clientSock.setUseClientMode(true); 

    } 

    public void run() throws IOException 
    { 
     InputStream input = null; 
     OutputStream output = null; 

     output = clientSock.getOutputStream(); 
     BufferedOutputStream bufferedOutput = new BufferedOutputStream(output); 
     bufferedOutput.write("Alice: is running".getBytes()); 
     bufferedOutput.flush(); 

    } 

} 

Кстати, Windows поддерживает только TLSv1 в соответствии с журналом, что удивительно.

+0

Что делать, если 'getAcceptedIssuers()' возвращает пустой массив, а не 'null'? – ZhongYu

+0

Кроме того, вместо использования подхода, основанного на доверии, вы можете использовать хранилище ключей A как доверительное хранилище B и наоборот. – ZhongYu

+0

спасибо, я просто попытался добавить пустой массив в getAcceptedIssuers() в код клиента:Публичный X509Certificate [] getAcceptedIssuers() { \t return newX509Certificate [] {}; } Но он по-прежнему вызывает ту же ошибку. Использование TrustManager [] trustClientCerts может динамически проверять сертификаты, если сертификаты добавляются/удаляются во время выполнения. Я добавил трассировку стека ошибок в моем исходном сообщении. – frogcd

ответ

0

Метод getAcceptedIssuers() может не возвращать null. См. Javadoc.

Но не используйте этот ненадежный код доверительного управляющего, тем более, что у вас есть собственный магазин доверия.

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

+0

Спасибо. Я вижу, что доверие и хранилище ключей совсем другое. Java имеет довольно приятный криптографический материал, но понятия безопасности (например, хранилище ключей, truststore) несколько запутывают по сравнению с вещами Openssl. – frogcd

+0

Главным образом потому, что они решили использовать тот же формат для обоих. – EJP

2

Из документации X509TrustManager.getAcceptedIssuers():

Возвращает: ненулевым (возможно, пустой) массив приемлемых сертификатов эмитента ЦС.

Неудивительно, что если возвращение нулевого значения там (как и вы) вызывает NullPointerException в дальнейшем.

Действительно, constructor of sun.security.ssl.HandshakeMessage$CertificateRequest makes use of this array of CA certificates assuming it's not null.

Кроме того, если вы хотите более реалистичную попытку двухсторонней аутентификации, вы можете создать собственный тестовый центр сертификации и иметь надлежащую доверенность. Поведение, которое вы используете (пустой список), работает, но оно указано только как приемлемое в спецификациях TLS 1.1. (Минуя все целевые проверки редко хорошая идея так или иначе.)


От одного из ваших комментариев:

Я не выяснить использование getAcceptedIssuers. Не могли бы вы немного объяснить?

getAcceptedIssuers() используется только для создания списка приемлемых сертификатов ЦС, отправленных в TLS сообщении Certificate Request. Несмотря на то, что это массив сертификатов, на самом деле используются только соответствующие DN этих сертификатов.

Это очень похоже на SSLCADNRequestFile and SSLCADNRequestPath directives in Apache Httpd. Крайне редко бывает полезно сделать это отличным от списка в вашем доверенном магазине.

Что действительно используется для проверки: checkServerTrusted().


содержит доверенных сертификатов доверенных лиц, который загружается при запуске программы. Если мы собираемся добавлять/удалять записи в Truststore во время выполнения, есть ли способ сделать это?

При необходимости вы можете загрузить свою доверенную сеть программно, вместо использования свойств по умолчанию или системы, как описано в this answer. Затем создайте свой серверный сокет из инициализированного SSLContext.

Если вы можете закрыть и повторно открыть сокет сервера каждый раз, когда вам нужно изменить truststoreYou также может иметь что-то немного более сложным, когда вы реализовать свой собственный TrustManager, что делегаты свои звонки на другой TrustManager инициированную из KeyStore (ваш truststore) и TrustManagerFactory. Вы изменили делегированный TrustManager всякий раз, когда ваш магазин доверия изменится (возможно, вам придется учитывать возможные проблемы параллелизма).

+0

Truststore содержит сертификаты доверенных сторон, которые загружаются при запуске программы. Если мы собираемся добавлять/удалять записи в Truststore во время выполнения, есть ли способ сделать это? Цените. – frogcd

+0

все сертификаты в моем коде. getAcceptedIssuers() возвращает ненулевой (возможно, пустой) массив допустимых сертификатов CA-сертификата. Как понимать «приемлемый»? – frogcd

+1

См. [Спецификация TLS, 'certificate_authorities' в' Запрос сертификата'] (http://tools.ietf.org/html/rfc5246#section-7.4.4). По сути, это то, что рекламирует сервер, он может принять (хотя он все равно может отказаться, если ему не нравится сертификат по какой-либо причине, когда клиент представляет его). На практике пустой список позволяет большинству клиентов отправлять первый найденный. Затем нужно проверить 'CheckServerTrusted', чтобы проверить их так или иначе (например, против фиксированного списка известных сертификатов, если они все самозаверяются в вашем случае). – Bruno

Смежные вопросы