Генерация SSL-сертификата с использованием keytool в jdk - PullRequest
6 голосов
/ 26 сентября 2010

Файлы хранилища ключей, которые я использовал в своем веб-приложении, истекли на прошлой неделе. Я создал это давным-давно. Поэтому я начал генерировать новый сертификат, используя keytool. Я использовал этот сертификат для соединения сервера транзакций и веб-сервера. Я хотел использовать самоподписанный сертификат для этого приложения. Я генерирую его, используя следующую команду, чтобы сгенерировать самозаверяющий ключ для сервера транзакций.

keytool -genkey -keystore keys/SvrKeyStore -keyalg rsa -validity 365 -alias Svr -storepass 123456 -keypass abcdefg -dname "CN=One1, OU=Development1, O=One, L=Bamba, S=Western Prov1, C=S1"

после коммнада для создания хранилища ключей для веб-приложения

keytool -genkey -keystore keys/ClientKeyStore -keyalg rsa -validity 365 -alias Web -storepass 123456 -keypass abcdefg -dname "CN=One, OU=Development, O=One, L=Bamba, S=Western Prov, C=SL"

Я использовал следующий код на сервере транзакций для создания соединения с сокетом

          String KEYSTORE = Config.KEYSTORE_FILE;//SvrKeyStore  keystore file
          char[] KEYSTOREPW = "123456".toCharArray();
          char[] KEYPW = "abcdefg".toCharArray();
          com.sun.net.ssl.TrustManagerFactory tmf;

          boolean requireClientAuthentication;

          java.security.Security.addProvider(new com.sun.net.ssl.internal.ssl.
                                             Provider());
          java.security.KeyStore keystore = java.security.KeyStore.getInstance(
              "JKS");
          keystore.load(new FileInputStream(KEYSTORE), KEYSTOREPW);

          com.sun.net.ssl.KeyManagerFactory kmf = com.sun.net.ssl.
              KeyManagerFactory.getInstance("SunX509");
          kmf.init(keystore, KEYPW);

          com.sun.net.ssl.SSLContext sslc = com.sun.net.ssl.SSLContext.
              getInstance("SSLv3");
          tmf = com.sun.net.ssl.TrustManagerFactory.getInstance("sunx509");
          tmf.init(keystore);

          sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
          SSLServerSocketFactory ssf = sslc.getServerSocketFactory();
          SSLServerSocket ssocket = (SSLServerSocket) ssf.createServerSocket(port);
          ssocket.setNeedClientAuth(true);

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

javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLHands
hakeException: java.security.cert.CertificateException: Untrusted Server Certifi
cate Chain
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.checkEOF(SSLSocketImpl.jav
a:1172)
        at com.sun.net.ssl.internal.ssl.AppInputStream.read(AppInputStream.java:
65)
        at net.schubart.fixme.internal.MessageInput.readExactly(MessageInput.jav
a:166)
        at net.schubart.fixme.internal.MessageInput.readMessage(MessageInput.jav
a:78)
        at cc.aot.itsWeb.ClientWriterThread.run(ClientWriterThread.java:241)
        at java.lang.Thread.run(Thread.java:619)
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateEx
ception: Untrusted Server Certificate Chain
        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1
520)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:182)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:176)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Clien
tHandshaker.java:975)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHa
ndshaker.java:123)
        at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:5
11)
        at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.jav
a:449)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.j
ava:817)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SS
LSocketImpl.java:1029)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.
java:621)
        at com.sun.net.ssl.internal.ssl.AppOutputStream.write(AppOutputStream.ja
va:59)
        at java.io.OutputStream.write(OutputStream.java:58)

Пожалуйста, кто-нибудь может сказать мне, в чем проблема

Ответы [ 3 ]

11 голосов
/ 26 сентября 2010

Во-первых, избегайте прямого использования пакетов и классов com.sun.net.ssl.Архитектура JSSE построена так, что вы можете использовать фабрики и указать поставщиков позже.Вместо этого используйте javax.net.ssl.TrustManagerFactory (то же самое для KeyManagerFactory и SSLContext).(Я бы предложил использовать "PKIX" вместо "SunX509" для алгоритма диспетчера доверия, так как обычно он используется по умолчанию у провайдера Sun, или лучше использовать TrustManagerFactory.getDefaultAlgorithm()).

Во-вторых, вы невам не нужно настраивать менеджер ключей на стороне клиента, если вы не используете аутентификацию по сертификату клиента.

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

Когда вы генерируете сертификат, вы должны убедиться, что вы используете CN=the.server.host.name.

keytool -genkey -keystore server-keystore.jks -alias server_alias \
        -dname "CN=the.server.host.name,OU=whateveryoulike" \
        -keyalg "RSA" -sigalg "SHA1withRSA" -keysize 2048 -validity 365

keytool -export -keystore server-keystore.jks -alias server_alias -file server.crt

keytool -import -keystore client-truststore.jks -file server.crt

Если вы хотите использовать аутентификацию по сертификату клиента, вам нужно повторить операцию, заменив хранилище ключей сервера и хранилище доверенных сертификатов на хранилище ключей клиента и хранилище доверенных сертификатов сервера соответственно.

В этом случае server-keystore.jks и server-truststore.jks могут быть одним и тем же файлом, но вам не нужно (то же самое на стороне клиента).

0 голосов
/ 26 сентября 2010

Чтобы понять это, вы должны понять, как работают сертификаты.

Поскольку любой может создать сертификат (как вы это сделали), недостаточно просто создать его - сертификаты должны быть доверенными. Сертификат является доверенным, только если он подписан другим сертификатом, которому доверяют. На вершине трастовой «пищевой цепи» находятся несколько крупных центров сертификации, которым вы можете заплатить деньги, чтобы ваш сертификат был «публично доверенным» (на вашем компьютере установлены сертификаты).

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

Проверьте, как ваш API позволяет вам определить, где находятся ваши доверенные сертификаты.

0 голосов
/ 26 сентября 2010

Все, что вам нужно было сделать, это keytool -selfcert - псевдоним XXX -valid DDD, где XXX - тот же псевдоним, что и раньше, а DDD - количество дней до истечения срока действия для сертификата сервера, затем экспортировать этот сертификат и импортировать в доверенное хранилище. Повторите в обратном порядке для клиента. Вы оставили часть экспорта / импорта.

Однако большая часть этого кода сейчас устарела. Вам не нужно вызывать addProvider (), и вы можете изменить com.sun.net.ssl ​​на javax.net.ssl ​​до конца.

...