java безопасный сокет без аутентификации? - PullRequest
2 голосов
/ 06 февраля 2011

У меня есть тривиальная программа-клиент для сокетов.
Для сертификатов сервера я создал хранилище ключей, используя 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, указывающее на хранилище ключей, которое я создал и которое использует мой сервер, связь будет в порядке!
Программа работает без исключений, и данные отправляются нормально, с клиента на сервер.


UPDATE:
Вот фрагмент кода, который я использую для включения анон-шифров (я сделал это для серверной и клиентской части):

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)

Спасибо

Ответы [ 2 ]

5 голосов
/ 06 февраля 2011

Вы правы, *_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]);
0 голосов
/ 06 февраля 2011

Вы имеете дело с сертификатами, созданными вами, то есть вы являетесь Центром сертификации.

Ваша проблема в том, что для того, чтобы обе стороны пожали друг другу руки и общались, им нужно доверять сертификатам друг друга,Доверие устанавливается путем построения цепочки от сертификата до ЦС, чтобы определить, доверяет ли конкретная сторона этому ЦС (могут быть также выполнены некоторые дополнительные шаги, такие как проверка, не был ли отозван сертификат).Большинство основных CA доверены по умолчанию в Java.В вашем случае ваш пользовательский центр сертификации не является.

Это означает, что ваш сертификат сервера должен находиться в хранилище доверенных сертификатов вашего клиента (как вы упомянули, указав, что это решает проблему).Это или сертификат CA (лучший выбор).

Посмотрите здесь общие сведения о SSL / TLS.

Если вы имеете дело с взаимным доверием, тогдаваш сервер также должен доверять центру сертификации, выдавшему ваш клиентский сертификат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...