簡體   English   中英

Java安全套接字無需身份驗證?

[英]java secure socket without authentication?

我有一個簡單的安全套接字服務器-客戶端程序。
對於服務器證書,我使用keytool創建了密鑰庫。
當我嘗試通過客戶端連接到服務器時,出現以下異常:
在服務器中:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown

在客戶中:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)

如果我的理解是正確的,則這些例外是由於我正在使用自己創建的證書而引起的。
我的問題如下:
如果我在服務器和客戶端中都設置了啟用的密碼套件,則所有* _anon *密碼套件都不能解決問題嗎?
我的意思是,如果我啟用*_anon_*密碼套件,則不需要身份驗證,因此也沒有例外。
這個對嗎?
因為我仍然有例外。 我嘗試在所有啟用的密碼套件中都包含所有已啟用的_anon。 沒有成功 我嘗試只設置匿名的,並得到了一個新的例外:

Exception in thread "main" java.lang.IllegalArgumentException: Name must not be null

有人可以解釋一下為什么使用匿名密碼套件獲得這些例外嗎?
注意:
如果在客戶端上設置系統屬性javax.net.ssl.trustStore指向我創建並由服務器使用的密鑰庫,則通信正常!
該程序無一例外都可以正常工作,並且數據可以從客戶端正確發送到服務器。


更新:
這是我用來啟用匿名密碼的代碼段(我已經在服務器和客戶端部分完成了此操作):

String[] supported = server.getSupportedCipherSuites();
String[] anonCipherSuitesSupported = new String[supported.length];
int count = 0;

for(int i = 0; i < supported.length; i++)
{
    if(supported[i].indexOf("_anon_") > 0)
    {
        anonCipherSuitesSupported[count++] = supported[i];
    }
}

String[] oldEnabled = server.getEnabledCipherSuites();
String[] newEnabled = new String[oldEnabled.length + count];
System.arraycopy(oldEnabled, 0, newEnabled, 0, oldEnabled.length);
System.arraycopy(anonCipherSuitesSupported, 0, newEnabled, oldEnabled.length, count);
server.setEnabledCipherSuites(newEnabled);

堆棧跟蹤位於客戶端:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.AppOutputStream.write(Unknown Source)
    at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlush(Unknown Source)
    at sun.nio.cs.StreamEncoder.flush(Unknown Source)
    at java.io.OutputStreamWriter.flush(Unknown Source)
    at com.client.SSLClient1.main(SSLClient1.java:58)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
    at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
    at sun.security.validator.Validator.validate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    ... 14 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
    at java.security.cert.CertPathBuilder.build(Unknown Source)
    ... 20 more

在服務器端:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.AppInputStream.read(Unknown Source)
    at com.sun.net.ssl.internal.ssl.AppInputStream.read(Unknown Source)
    at com.server.SecureOrderTaker.main(SecureOrderTaker.java:92) 

現在,如果我只是這樣做:

server.setEnabledCipherSuites(anonCipherSuitesSupported);

這樣就只啟用了匿名密碼套件

Exception in thread "main" java.lang.IllegalArgumentException: Name must not be null
    at com.sun.net.ssl.internal.ssl.CipherSuite.valueOf(Unknown Source)
    at com.sun.net.ssl.internal.ssl.CipherSuiteList.<init>(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLServerSocketImpl.setEnabledCipherSuites(Unknown Source)
    at com.server.SecureOrderTaker.main(SecureOrderTaker.java:82)

謝謝

沒錯, *_anon_*密碼用於完整的未經身份驗證的連接(服務器和客戶端都是匿名的)。 使用這些密碼套件,不需要證書。 我寫了一個小代碼來測試:

ServerSocketFactory sf = SSLServerSocketFactory.getDefault();
final SSLServerSocket socket = (SSLServerSocket)sf.createServerSocket(443);
System.out.println(Arrays.toString(socket.getSupportedCipherSuites()));
System.out.println(Arrays.toString(socket.getEnabledCipherSuites()));

socket.setEnabledCipherSuites(new String[] {"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"});

Thread t = new Thread() {
    public void run() {
        try {
            Socket client = socket.accept();
            client.getOutputStream().write("Hello World\n".getBytes("ASCII"));
            client.close();
        } catch (IOException ioe) {
        }
    }
};

t.start();
Thread.sleep(2000);
SSLSocket client = (SSLSocket) SSLSocketFactory.getDefault().createSocket("localhost", 443);
client.setEnabledCipherSuites(new String[] {"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"});
InputStream in = client.getInputStream();
byte[] data = new byte[1024];
int len = in.read(data);
System.out.println(new String(data, 0, len));

我知道這段代碼並不完美,但是我成功地在客戶端和服務器之間交換了數據。 也許您的服務器或客戶端套接字配置不正確。 你能給你完整的堆棧跟蹤嗎?

請注意,不建議使用這些密碼,因為它們容易受到中間人攻擊。

更新 :我發現了問題。 anonCipherSuitesSupported數組長度過長。 因此,在添加*_anon_*該數組以一堆null值結尾。 而且該實現似乎未在啟用的密碼列表中接受null

String[] supported = server.getSupportedCipherSuites();
List<String> list= new ArrayList<String>();

for(int i = 0; i < supported.length; i++)
{
    if(supported[i].indexOf("_anon_") > 0)
    {
        list.add(supported[i]);
    }
}
String[] anonCipherSuitesSupported = list.toArray(new String[0]);

您正在處理自己創建的證書,這意味着您是證書頒發機構。

您的問題是,為了使雙方握手和溝通,他們都需要彼此信任對方的證書。 通過建立從證書到CA的鏈來建立信任關系,以查看特定方是否信任該CA(也可以執行一些其他步驟,例如檢查證書是否被吊銷)。 默認情況下,大多數主要的CA在Java中都是受信任的。 在您的情況下,您的自定義CA不是。

這意味着您的服務器證書應該位於客戶端的信任庫中(如您所提到的,指向它可以解決問題)。 那就是CA的證書(更好的選擇)。

在此處查看有關SSL / TLS的一般詳細信息。

如果您正在處理相互信任,那么您的服務器也應該信任頒發您的客戶端證書的CA。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM