Это никогда не работало раньше - я настраиваю среду впервые.
НЕСКОЛЬКО ОБНОВЛЕНИЙ НИЖЕ
Мой RMI-сервер запущен в типе процесса «демон», потому что я хочу, чтобы он запускался при запуске системы как служба (без использования функции xinitd), поэтому я могу использовать SSL и давать команды к демону, относящемуся к тому, что входит в реестр через отдельный механизм. (IOW, я не использую что-то вроде Runtime.getRuntime().exec("rmiregistry 2020");
)
Я знаю, что сервер RMI запущен и работает, потому что это работает:
telnet localhost <port>
Мой клиентский код кодирует множество переменных среды для SSL в командную строку, чтобы запустить его - позже я сделаю это программно, но сейчас он начинается с:
$ java -Djavax.net.ssl.trustStore=/var/RMIssl/truststore -Djavax.net.ssl.trustStorePassword=<pwd> -Djava.rmi.server.hostname=localhost my.package.Test
Сначала все, что я получил, было:
java.rmi.ConnectException: Connection refused to host: localhost; nested excepti
on is:
java.net.ConnectException: Connection refused
, который был в значительной степени бесполезен, за исключением того, что произошла ошибка. Итак, я дважды проверил все вещи и, наконец, добавил несколько операторов трассировки со сбросами и добавил это в командную строку:
-Djavax.net.debug=all
Это очень помогло, но было все еще трудно определить, где именно была ошибка, поэтому я поместил подозреваемую строку в цикл, перехватывая исключение, и теперь ясно, что это поиск конкретного объекта в реестре, который проблема, хотя сообщение об ошибке наверняка вводит в заблуждение. Вот что я получил:
$ java -Djavax.net.debug=all -Djavax.net.ssl.trustStore=/var/RMIssl/truststore -Djavax.net.ssl.trustStorePassword=<pwd> -Djava.rmi.server.hostname=localhost my.package.Test
Setup the security manager.
Find the registry.
Lookup testClient.
keyStore is :
keyStore type is : jks
keyStore provider is :
init keystore
init keymanager of type SunX509
trustStore is: /var/RMIssl/truststore
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
Subject: CN= <my cert>
Issuer: CN= <my cert>
Algorithm: RSA; Serial number: 0x<number>
Valid from <dates>
trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
java.net.ConnectException: Connection refused
enter 'quit' to exit:
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
java.net.ConnectException: Connection refused
enter 'quit' to exit:
Обратите внимание, что структура цикла позволяет лучше анализировать ошибки; вывод относительно склада доверенных сертификатов и моего сертификата (ов) не повторяется.
Это говорит о том, что он не находит подходящего набора шифров для использования, но все это в одной системе! Клиент и сервер одновременно ищут одни и те же хранилища доверенных сертификатов и т. Д.
Если кто-то считает это полезным, вот код клиента:
import java.rmi.*;
import java.rmi.registry.*;
import java.io.*;
import java.net.*;
import javax.rmi.ssl.*;
public class Test
{
private static String ServerHost = "localhost";
private static int ServerPort = 6543; private static Registry registry;
private static testClient c;
public static void main(String[] args) throws Exception
{
msg("Setup the security manager.");
System.setSecurityManager(new RMISecurityManager());
msg("Find the registry.");
registry = LocateRegistry.getRegistry(ServerHost, ServerPort,
new SslRMIClientSocketFactory());
msg("Lookup testClient.");
boolean keepLooping = true;
while (keepLooping)
{
try
{
c = (testClient)registry.lookup(testClient.class.getSimpleName());
}
catch (Exception e)
{
msg(e.toString());
}
keepLooping = !(Prompt("enter 'quit' to exit: ").equals("quit"));
}
}
public static void msg(String message)
{
System.out.println(message); System.out.flush();
}
public static String Prompt(String prompt)
{
String out = "";
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try
{
System.out.print(prompt);
out = br.readLine();
}
catch (IOException e)
{
msg("IO error reading from standard-in: "+e.toString());
}
return out;
}
}
ОБНОВЛЕНИЕ ОДИН:
Следуя своей догадке, я обнаружил переменную окружения, используемую для установки наборов шифров, используемых на фабриках сокетов SslRMI. Итак, я добавил это на стороне сервера:
javax.rmi.ssl.client.enabledCipherSuites="TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,TLS_DHE_DSS_WITH_AES_256_CBC_SHA,TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA256"
Это фактически весь список, который был либо "недоступен", либо "не поддерживается" - возможно, это излишне, но я хочу, чтобы сервер брал все, что есть у клиента.
Это значительно изменило вывод! Тем не менее, он все еще зависает от того же оператора, на этот раз, с отключенной трассировкой, мы получаем:
java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
Когда я добавляю трассировку, есть много данных о том, какой протокол был фактически выбран и что он с этим делает, и в конечном итоге он доходит до этого (сильно обрезается перед этим отрывком):
...
0080: 00 0F 00 10 00 11 00 02 00 12 00 04 00 05 00 14 ................
0090: 00 08 00 16 00 0B 00 02 01 00 ..........
main, received EOFException: error
main, handling exception: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
main, SEND TLSv1 ALERT: fatal, description = handshake_failure
main, WRITE: TLSv1 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 01 00 02 02 28 ......(
main, called closeSocket()
java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
ОБНОВЛЕНИЕ ВТОРОЕ:
По предложению EJP, которого я уважаю в этой теме, из его ответов другим, я проверил предпосылку, что сервер на самом деле не работает с реестром, или, по крайней мере, не в комбинации системы / порта, где клиент ищет.
Чтобы проверить эту гипотезу, я просто запускаю и останавливаю сервер, и поведение клиентов существенно меняется. В случае, когда он НЕ работает, я получаю длинный блок поочередно «Игнорирование недоступных» или «Игнорирование неподдерживаемых» наборов шифров, за которыми следуют их имена, как я уже сообщал в первом случае выше.
Когда сервер работает, клиент дает совсем другой ответ, отправляя мне очень подробную информацию об особенностях создания зашифрованного соединения, как я сообщал в моем первом обновлении выше. Кроме того, в нем говорится, что удаленный хост закрыл соединение во время рукопожатия, поэтому очевидно, что рукопожатие происходило, когда оно прекращалось. Дополнительно я получаю это:
$ netstat -n -l | grep <my port number>
tcp 0 0 :::<my port number> :::* LISTEN
Я думаю, это убедительно ...
ОБНОВЛЕНИЕ ТРИ:
Я заметил, что вывод несколько изменился, поэтому я должен уточнить, и т. Д. В настоящее время, когда я тестирую, и я уверен, что на моем порте есть прослушиватель в моей системе из моего реестра, который (надеюсь) запущен правильно, используятот же файл хранилища доверенных сертификатов и хранилища ключей, что и у клиента, я запускаю клиент и получаю МНОГО информации о трассировке из SSL, включая список шифров, как указано выше, а затем в нем перечисляется RandomCookie, пустой идентификатор сеанса, другой список шифров, некоторыеэллиптические кривые, и выводит кучу байтов, вот где появляется следующий текст:
0080: 00 0F 00 10 00 11 00 02 00 12 00 04 00 05 00 14 ................
0090: 00 08 00 16 00 0B 00 02 01 00 ..........
[Raw read]: length = 5
0000: 15 03 01 00 02 .....
[Raw read]: length = 2
0000: 02 28 .(
main, READ: TLSv1 Alert, length = 2
main, RECV TLSv1 ALERT: fatal, handshake_failure
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Было бы неплохо, если бы мы получили чистое сообщение об ошибке, ПОЧЕМУ было отброшено рукопожатие.НОТАБИЛЬНО, я не выяснил, как получить аналогичный вывод с сервера - куда это деваться?Я предоставил ему javax.net.debug = all?!?!
ОБНОВЛЕНИЕ ЧЕТВЕРТАЯ:
Я задавался вопросом, была ли проблема с объектом, который я 'Мы зарегистрированы в Реестре RMI.Мой оригинальный дизайн - создать два объектных интерфейса.Первый получает экземпляр и регистрируется.Его работа заключается в создании других объектов, которые были созданы, но НЕ зарегистрированы;клиент получает первый объект из реестра и запрашивает у него второй, и этот экземпляр является частным для этого клиента.Затем клиент вызывает методы, предоставляемые вторым объектом, которые выполняются на стороне сервера, и результаты передаются обратно.
Чтобы убедиться, что это не играет роли, я создал следующее.Обратите внимание, однако, что это ничего не изменило - оно намного короче и, следовательно, более уместно включить сюда для полного раскрытия:
package <myPackage>;
import <myPackage>.*;
import java.rmi.*;
public interface testobject extends Remote
{
myAPI.Person getObject() throws RemoteException;
}
Затем я использовал интерфейс следующим образом:
package <myPackage>;
import <myPackage>.myAPI;
import <myPackage>.testobject;
import java.rmi.*;
import java.rmi.server.*;
public class testObject extends UnicastRemoteObject implements testobject
{
public testObject() throws RemoteException
{
// Set the security manager to the RMI security manager, to prevent
// the client process from using the server in a malicious way.
try
{
if (System.getSecurityManager() == null)
{
System.setSecurityManager(new RMISecurityManager());
}
}
catch(Exception e)
{
System.out.println("SecurityManager load error: \n"+e);
}
}
public myAPI.Person getObject() throws RemoteException
{
myAPI c = new myAPI();
myAPI.Person TestObject = c.newPerson();
TestObject.FirstName = "Test";
TestObject.LastName = "Object";
return TestObject;
}
}
Затем я обновил код сервера следующими строками:
try
{
testObject TestObject = new testObject();
OK = true;
}
catch (RemoteException e)
{
msg("Error instantiating TestObject: \n"+e.toString());
}
if (OK)
{
name = testObject.class.getSimpleName();
try
{
registry.bind(name, ServerProcess);
msg("New testObject has been bound to the RMI registry.");
}
catch(Exception e)
{
msg("The new testObject could not be bound to the RMI registry: \n"+e.toString());
OK = false;
}
}
И, наконец, клиент с:
testObject to;
try
{
msg("Now trying a 'testObject'...");
to = (testObject)registry.lookup(testObject.class.getSimpleName());
msg("Well! WE DID NOT THROW AN EXCEPTION!");
person = to.getObject();
msg("Name: "+person.FirstName+" "+person.LastName);
}
catch (Exception e)
{
msg(e.toString());
}
К сожалению, он по-прежнему выдает (то же самое) исключение.
Продолжая, "хммм ..."