2012-01-20 26 views
6

Tôi đang làm việc trên một dự án mà tôi muốn thêm SSL vào, vì vậy tôi đã tạo một thử nghiệm máy chủ/máy khách đơn giản để xem nó có hoạt động hay không và tôi nhận được NoSuchAlgorithmException. Sau đây là mã máy chủ của tôi mà là ném ngoại lệ:Mã Java SSL ném NoSuchAlgorithException

import java.io.*; 
import java.net.*; 
import java.security.KeyManagementException; 
import java.security.KeyStore; 
import java.security.KeyStoreException; 
import java.security.NoSuchAlgorithmException; 
import java.security.SecureRandom; 
import java.security.UnrecoverableKeyException; 
import java.security.cert.CertificateException; 

import javax.net.ssl.*; 

public class SslServer 
{ 
    private static final int PORT = 5555; 

    public static void main(String[] args) 
    { 
     SecureRandom sr = new SecureRandom(); 
     sr.nextInt(); 

     try { 
      //client.public is the keystore file that holds the client's public key (created with keytool) 
      KeyStore clientKeyStore = KeyStore.getInstance("JKS"); 
      clientKeyStore.load(new FileInputStream("client.public"), "clientpublicpw".toCharArray()); 

      //server.private is the key pair for the server (created with keytool) 
      KeyStore serverKeyStore = KeyStore.getInstance("JKS"); 
      clientKeyStore.load(new FileInputStream("server.private"), "serverprivatepw".toCharArray()); 

      TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 
      tmf.init(clientKeyStore); 

      //This next line is where the exception occurs 
      KeyManagerFactory kmf = KeyManagerFactory.getInstance("TLS"); 
      kmf.init(serverKeyStore, "serverprivatepw".toCharArray()); 

      SSLContext sslContext = SSLContext.getInstance("TLS"); 
      sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), sr); 

      SSLServerSocketFactory sf = sslContext.getServerSocketFactory(); 
      SSLServerSocket ss = (SSLServerSocket)sf.createServerSocket(SslServer.PORT); 
      ss.setNeedClientAuth(true); 

      BufferedReader in = new BufferedReader(new InputStreamReader(ss.accept().getInputStream())); 

      String line = null; 
      while((line = in.readLine()) != null) 
      { 
       System.out.println(line); 
      } 
      in.close(); 
      ss.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
     } catch (CertificateException e) { 
      e.printStackTrace(); 
     } catch (KeyStoreException e) { 
      e.printStackTrace(); 
     } catch (UnrecoverableKeyException e) { 
      e.printStackTrace(); 
     } catch (KeyManagementException e) { 
      e.printStackTrace(); 
     } 
    } 

} 

các stacktrace tôi nhận được là:

java.security.NoSuchAlgorithmException: TLS KeyManagerFactory not available 
    at sun.security.jca.GetInstance.getInstance(Unknown Source) 
    at javax.net.ssl.KeyManagerFactory.getInstance(Unknown Source) 
    at SslServer.main(SslServer.java:32) 

tôi đã cố gắng thay thế "TLS" với "SSL" và tôi vẫn có những ngoại lệ như vậy. Điều đó không có ý nghĩa với tôi. TLS và SSL không được hỗ trợ như thế nào? Đây là lần đầu tiên tôi thử triển khai SSL và có vẻ như khó tìm được tài nguyên tốt về điều này với các ví dụ mã được giải thích rõ ràng. Bất cứ ai có thể cho tôi biết lý do tại sao tôi nhận được ngoại lệ này hoặc chỉ ra một cái gì đó sai trái với mã của tôi?

Trả lời

15

Có một số vấn đề:

  • Nó được gọi là TLS (Transport Layer Security), không TSL (đối với SSLContext).
  • tôi muốn đề nghị sử dụng mặc định ở đây: TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) (Mặc định sẽ PKIX trên Oracle JRE`)
  • (EDIT:) Giá trị mặc định là KeyManagerFactorySunX509 (TLS không tồn tại ở đây). Một lần nữa, sử dụng getDefaultAlgorithm().
  • Bạn nên đóng FileInputStream khi đã đọc chúng.
  • Không rõ lý do tại sao bạn có cả khách hàng và kho khóa máy chủ ở cùng một nơi. Đây nên là hai chương trình: một cho khách hàng và máy chủ (và setNeedClientAuth(true) chỉ hữu ích ở phía máy chủ). Nó sẽ được rõ ràng hơn để gọi nó là cái gì khác hơn là "cửa hàng của khách hàng" nếu nó có hiệu quả kho khóa của bạn. (Ngoài ra, vì bạn dường như đang học cách thực hiện công việc này, tôi khuyên bạn nên thử mà không cần xác thực chứng chỉ ứng dụng khách trước, trong trường hợp đó, máy chủ sẽ không cần một truststore: sử dụng null làm tham số thứ hai của SSLContext.init(...) để sử dụng giá trị mặc định.)
  • KHÔNG cung cấp khóa máy chủ cho máy khách. Chỉ xuất chứng chỉ của nó vào kho khóa mới mà bạn sẽ sử dụng làm cửa hàng tin cậy. Mỗi thực thể (máy khách và máy chủ) nên giữ riêng các khóa riêng của chúng.
  • Không quá nhiều khóa công khai công khai (chỉ) của bên từ xa mà bạn muốn trong cửa hàng tin cậy của bạn: nó sẽ là chứng chỉ của nó. Đảm bảo rằng bạn không chỉ nhập khóa công khai của nó, mà là toàn bộ chứng chỉ.
  • Để làm rõ, hãy giữ các phần mở rộng thích hợp cho tệp của bạn: sử dụng .jks cho kho khóa JKS của bạn, điều này sẽ giúp bạn giảm đau đầu sau này.
  • Bạn có thể sử dụng null cho số SecureRandom trong SSLContext.init(...): điều này sẽ sử dụng giá trị mặc định theo nhà cung cấp bảo mật.

Something như thế này nên làm việc tốt hơn:

KeyStore trustStore = KeyStore.getInstance("JKS"); 
InputStream tsis = new FileInputStream("trustedcerts.jks"); 
trustStore.load(tsis, "clientpublicpw".toCharArray()); 
tsis.close(); 

KeyStore serverKeyStore = KeyStore.getInstance("JKS"); 
InputStream ksis = new FileInputStream("server.jks"); 
clientKeyStore.load(ksis.close(), "serverprivatepw".toCharArray()); 
ksis.close(); 

TrustManagerFactory tmf = 
    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
tmf.init(trustStore); 

KeyManagerFactory kmf = 
    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
kmf.init(serverKeyStore, "serverprivatepw".toCharArray()); 

SSLContext sslContext = SSLContext.getInstance("TLS"); 
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 

SSLServerSocketFactory sf = sslContext.getServerSocketFactory(); 
SSLServerSocket ss = (SSLServerSocket)sf.createServerSocket(SslServer.PORT); 
ss.setNeedClientAuth(true); 
+0

k tôi đã chỉnh sửa bài đăng của mình để thêm thay đổi đó. Tôi vẫn nhận được cùng một ngoại lệ ngay cả khi tôi sử dụng "TLS" – MattL922

+0

Nếu bạn đang thực sự lo lắng về mật khẩu, bạn nên sử dụng một 'char []' trực tiếp hoặc như gọi lại mật khẩu, và không bao giờ mã hóa mật khẩu của bạn anyway trong một ứng dụng thực (xem http://stackoverflow.com/a/8889285/372643). – Bruno

+0

Là một lưu ý phụ, chờ 'readLine' trả về' null' không phải là cách hay để biết khi nào nên dừng đọc (từ bất kỳ loại socket nào), hãy xem thảo luận tại đây: http://stackoverflow.com/a/6425509/372643 – Bruno

1

Tên SSLContext đúng là "TLS".Danh sách các tên thuật toán chuẩn có thể được tìm thấy here.

+0

Đây là một loại nhập sai và đã được sửa. Tôi vẫn nhận được ngoại lệ với "TLS" – MattL922

+0

Xem chỉnh sửa của Bruno đối với câu trả lời của ông về KeyManagerFactory, đó sẽ là vấn đề khác của bạn. –

2

Xem http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#SupportClasses để biết ví dụ và tên của thuật toán được hỗ trợ. Có vẻ như "SunX509" và "NewSunX509" là các thuật toán được KeyManagerFactory hỗ trợ. Và giao thức được đặt tên là TLS chứ không phải TSL.

+0

Một lần nữa, đây là một sai lầm và đã được sửa. Tôi vẫn nhận được ngoại lệ với "TLS" – MattL922

+0

Bạn đã đọc câu trả lời của tôi chưa? * Có vẻ như "SunX509" và "NewSunX509" là các thuật toán được KeyManagerFactory hỗ trợ *. –

+0

+ 1'ed (như tôi đã thực hiện một lỗi đánh máy trong câu trả lời ban đầu của tôi 'X509' ->' SunX509'). @ MattL922: 'getDefaultAlgorithm()' thường là cược an toàn nhất. – Bruno

Các vấn đề liên quan