Keytool создает доверенный самозаверяющий сертификат - PullRequest
6 голосов
/ 04 февраля 2010

Я пытаюсь использовать (java) keytool для создания самозаверяющего сертификата, но когда я пытаюсь использовать его, я получаю следующее исключение (полное описание см. Внизу).

...<5 more exceptions above this>
Caused by: sun.security.validator.ValidatorException: No trusted certificate found
        at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)
        at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)
        at sun.security.validator.Validator.validate(Validator.java:203)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
        at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
        ... 22 more

Я знаю, что могу обойти это с помощью этого кода:

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;

HostnameVerifier hv = new HostnameVerifier() {
    public boolean verify(String urlHostName, SSLSession session) {
        System.out.println("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost());
        return true;
    }
};

HttpsURLConnection.setDefaultHostnameVerifier(hv);

(источник)

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

Кто-нибудь может указать мне правильное направление? Сейчас я тестирую локально, так что довольно легко что-то изменить. У меня есть доступ к коду сервера, коду клиента и к файлу .keystore.

Обновление

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

server.keystore

hostname[username:/this/is/a/path][711]% keytool -list -keystore server.keystore -v
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: hostname
Creation date: Feb 4, 2010
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Issuer: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Serial number: 4b6b0ea7
Valid from: Thu Feb 04 13:15:03 EST 2010 until: Wed May 05 14:15:03 EDT 2010
Certificate fingerprints:
         MD5:  81:C0:3F:EC:AD:5B:7B:C4:DA:08:CC:D7:11:1F:1D:38
         SHA1: F1:78:AD:C8:D0:3A:4C:0C:9A:4F:89:C0:2A:2F:E2:E6:D5:13:96:40
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************

client.truststore

hostname[username:/this/is/a/path][713]% keytool -list -keystore client.truststore -v
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: mykey
Creation date: Feb 4, 2010
Entry type: trustedCertEntry

Owner: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Issuer: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Serial number: 4b6b0ea7
Valid from: Thu Feb 04 13:15:03 EST 2010 until: Wed May 05 14:15:03 EDT 2010
Certificate fingerprints:
         MD5:  81:C0:3F:EC:AD:5B:7B:C4:DA:08:CC:D7:11:1F:1D:38
         SHA1: F1:78:AD:C8:D0:3A:4C:0C:9A:4F:89:C0:2A:2F:E2:E6:D5:13:96:40
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************

Обновление

Я подумал, что было бы полезно включить все исключение:

javax.xml.soap.SOAPException: java.io.IOException: Could not transmit message
        at org.jboss.ws.core.soap.SOAPConnectionImpl.callInternal(SOAPConnectionImpl.java:115)
        at org.jboss.ws.core.soap.SOAPConnectionImpl.call(SOAPConnectionImpl.java:66)
        at com.alcatel.tpapps.common.utils.SOAPClient.execute(SOAPClient.java:193)
        at com.alcatel.tpapps.common.utils.SOAPClient.main(SOAPClient.java:280)
Caused by: java.io.IOException: Could not transmit message
        at org.jboss.ws.core.client.RemotingConnectionImpl.invoke(RemotingConnectionImpl.java:192)
        at org.jboss.ws.core.client.SOAPRemotingConnection.invoke(SOAPRemotingConnection.java:77)
        at org.jboss.ws.core.soap.SOAPConnectionImpl.callInternal(SOAPConnectionImpl.java:106)
        ... 3 more
Caused by: org.jboss.remoting.CannotConnectException: Can not connect http client invoker. sun.security.validator.ValidatorException: No trusted certificate found.
        at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:368)
        at org.jboss.remoting.transport.http.HTTPClientInvoker.transport(HTTPClientInvoker.java:148)
        at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:141)
        at org.jboss.remoting.Client.invoke(Client.java:1858)
        at org.jboss.remoting.Client.invoke(Client.java:718)
        at org.jboss.ws.core.client.RemotingConnectionImpl.invoke(RemotingConnectionImpl.java:171)
        ... 5 more
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1584)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:848)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106)
        at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495)
        at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:877)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1089)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1116)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1100)
        at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:402)
        at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:170)
        at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:857)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)
        at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:288)
        ... 10 more
Caused by: sun.security.validator.ValidatorException: No trusted certificate found
        at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)
        at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)
        at sun.security.validator.Validator.validate(Validator.java:203)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
        at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
        ... 22 more

Ответы [ 4 ]

14 голосов
/ 04 февраля 2010

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

На стороне сервера:

keytool -keystore <keystore file> -alias <alias> -export -file <certfilename>.cert

Скопируйте файл .cert на сторону клиента и затем:

keytool -keystore <truststore file> -alias <alias> -import -file <certfilename>.cert
9 голосов
/ 20 июля 2013

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

Сертификаты в хранилище ключей ведут себя не одинаково, в зависимости от того, как вы их сгенерировали или импортировали.

Тип записи импортированного сертификата (видимый при подробном перечислении всего хранилища ключей с помощью -list -v) - "rustCertEntry ". Тип записи сгенерированного сертификата - «PrivateKeyEntry». При экспорте сертификата вы экспортируете только его открытый ключ и необязательную ссылку на его издателя.

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

Я бы не стал этого делать, потому что реализации SSL / TLS, вероятно, не поддерживают это. С точки зрения реального мира это похоже на развертывание секретного секретного ключа от Verisign на каком-то непонятном веб-сервере для подписи случайных страниц, тогда как единственная цель этого закрытого ключа - оставаться в безопасности и подписывать другие сертификаты. Разработчики SSL / TLS, вероятно, не будут загрязнять свой код таким случаем использования, и в любом случае расширение сертификата «KeyUsage» может ограничить использование сертификата подписью, предотвращая шифрование.

Именно поэтому я предлагаю перестроить цепочку сертификатов для вашего теста.

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

Временное хранилище their-keystore.jks представляет центр выдачи сертификатов. Я передаю его цепочкой сертификатов ca2 -> ca1 -> ca, а ca считается корневым сертификатом. Цепочка появляется с каждым некорневым сертификатом (а именно ca1 и ca2), ссылаясь на их эмитента как Certificate[2]. Обратите внимание, что каждый сертификат «PrivateKeyEntry».

Затем я подаю my-keystore.jks с этими сертификатами в следующем порядке: ca, ca1, ca2. Я импортирую ca с параметром -trustcacerts, что означает, что он становится корневым сертификатом. В my-keystore.jks каждый импортируемый сертификат теперь называется «rustCertEntry », что означает, что существует только открытый ключ. Отношение выдачи отображается только в поле «Эмитент», но оно в порядке, поскольку отношение доверия имело наибольшее значение во время импорта.

На данный момент my-keystore.jks имитирует среду, содержащую некоторые доверенные сертификаты, например, свежую JRE. their-keystore.jks имитирует владельцев тех сертификатов, которые имеют право подписывать запросы сертификатов.

Я тоже: я создаю самоподписанный сертификат e1 в my-keystore.jks, получаю его подписанным ca2 (через their-keystore.jks) и импортирую подписанный результат обратно в my-keystore.jks. e1 по-прежнему является «PrivateKeyEntry» (потому что его закрытый ключ остается в my-keystore.jks), но теперь я построил следующую цепочку: e1 -> ca2 -> ca1. Кажется, что ca1 -> ca подразумевается, когда ca является центром сертификации.

Для создания хранилища доверенных сертификатов я просто импортирую сертификаты ca, ca1 и ca2 так же, как я делал для my-keystore.jks. Обратите внимание, что я не импортирую e1, так как я ожидаю, что клиент SSL / TLS проверит его по ca2.

Я думаю, это довольно близко к тому, как все работает в реальном мире. Что здесь хорошо, так это то, что вы имеете полный контроль над сертификатами и не зависите от JRE.

Вот код, демонстрирующий то, что я говорю на практике. Кажется, работает с Jetty (клиент и сервер), пока вы отключите список отзыва сертификатов (тема оставлена ​​на другой день).

#!/bin/bash

rm  their-keystore.jks 2> /dev/null
rm  my-keystore.jks    2> /dev/null
rm  my-truststore.jks  2> /dev/null

echo "===================================================="
echo "Creating fake third-party chain ca2 -> ca1 -> ca ..."
echo "===================================================="

keytool -genkeypair -alias ca  -dname cn=ca                           \
  -validity 10000 -keyalg RSA -keysize 2048                           \
  -ext BasicConstraints:critical=ca:true,pathlen:10000                \
  -keystore their-keystore.jks -keypass Keypass -storepass Storepass

keytool -genkeypair -alias ca1 -dname cn=ca1                          \
  -validity 10000 -keyalg RSA -keysize 2048                           \
  -keystore their-keystore.jks -keypass Keypass -storepass Storepass

keytool -genkeypair -alias ca2 -dname cn=ca2                          \
  -validity 10000 -keyalg RSA -keysize 2048                           \
  -keystore their-keystore.jks -keypass Keypass -storepass Storepass


  keytool -certreq -alias ca1                                            \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass   \
| keytool -gencert -alias ca                                             \
    -ext KeyUsage:critical=keyCertSign                                   \
    -ext SubjectAlternativeName=dns:ca1                                  \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass   \
| keytool -importcert -alias ca1                                         \
    -keystore   their-keystore.jks -keypass Keypass -storepass Storepass

#echo "Debug exit" ; exit 0

  keytool -certreq -alias ca2                                           \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -gencert -alias ca1                                           \
    -ext KeyUsage:critical=keyCertSign                                  \
    -ext SubjectAlternativeName=dns:ca2                                 \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -alias ca2                                        \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass

keytool -list -v -storepass Storepass -keystore their-keystore.jks


echo  "===================================================================="
echo  "Fake third-party chain generated. Now generating my-keystore.jks ..."
echo  "===================================================================="
read -p "Press a key to continue."

# Import authority's certificate chain

  keytool -exportcert -alias ca                                         \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -trustcacerts -noprompt -alias ca                 \
    -keystore  my-keystore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca1                                        \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -noprompt -alias ca1                              \
    -keystore  my-keystore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca2                                        \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -noprompt -alias ca2                              \
    -keystore  my-keystore.jks -keypass Keypass -storepass Storepass

# Create our own certificate, the authority signs it.

keytool -genkeypair -alias e1  -dname cn=e1                        \
  -validity 10000 -keyalg RSA -keysize 2048                        \
  -keystore my-keystore.jks -keypass Keypass -storepass Storepass

  keytool -certreq -alias e1                                            \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass     \
| keytool -gencert -alias ca2                                           \
    -ext SubjectAlternativeName=dns:localhost                           \
    -ext KeyUsage:critical=keyEncipherment,digitalSignature             \
    -ext ExtendedKeyUsage=serverAuth,clientAuth                         \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -alias e1                                         \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass

keytool -list -v  -storepass Storepass -keystore  my-keystore.jks

echo "================================================="
echo "Keystore generated. Now generating truststore ..."
echo "================================================="
read -p "Press a key to continue."

  keytool -exportcert -alias ca                                        \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
| keytool -importcert -trustcacerts -noprompt -alias ca                \
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca1                                       \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
| keytool -importcert -noprompt -alias ca1                             \
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca2                                       \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
| keytool -importcert -noprompt -alias ca2                             \
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass

keytool -list -v  -storepass Storepass -keystore  my-truststore.jks

rm  their-keystore.jks 2> /dev/null
3 голосов
/ 15 февраля 2010

Ты не должен этого делать. Склад ключей является строго частным. Если вы даете кому-нибудь это, вы смертельно рискуете с безопасностью. Нет смысла делать подобные вещи, просто чтобы заставить их работать, потому что это не работает - это просто брешь в безопасности. Вы должны сделать это правильно: экспортируйте из хранилища ключей сервера в хранилище доверенных сертификатов клиента и из хранилища ключей клиента, если оно есть, в хранилище ключей сервера.

0 голосов
/ 04 февраля 2010

Я не понимаю. Используете ли вы хранилище ключей сервера с клиентом? Какой именно ваш вариант использования? Вы пытаетесь настроить взаимную аутентификацию?

Если да, то вы на неверном пути. Вам понадобится хранилище ключей клиента (для самозаверяющего сертификата и закрытого ключа клиента) и хранилище доверия клиента (для «автономного» самозаверяющего сертификата сервера, т.е. без его закрытого ключа). Оба они отличаются от хранилища ключей сервера.

...