Получено фатальное предупреждение: bad_certificate

Я пытаюсь настроить соединение SSL Socket (и делаю следующее на клиенте)

  1. Я создаю запрос на подпись сертификата для получения подписанного сертификата клиента.

  2. Теперь у меня есть закрытый ключ (используемый во время CSR), подписанный сертификат клиента и корневой сертификат (полученный вне очереди).

  3. Я добавляю закрытый ключ и подписанный сертификат клиента в цепочку сертификатов и добавляю их в диспетчер ключей. и корневой сертификат доверительному менеджеру. Но я получаю ошибку плохого сертификата.

Я почти уверен, что использую правильные сертификаты. Должен ли я также добавить подписанный сертификат клиента в менеджер доверия? Пробовал, пока не повезло.

//I add the private key and the client cert to KeyStore ks
FileInputStream certificateStream = new FileInputStream(clientCertFile);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
java.security.cert.Certificate[] chain = {};
chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
certificateStream.close();
String privateKeyEntryPassword = "123";
ks.setEntry("abc", new KeyStore.PrivateKeyEntry(privateKey, chain),
        new KeyStore.PasswordProtection(privateKeyEntryPassword.toCharArray()));

//Add the root certificate to keystore jks
FileInputStream is = new FileInputStream(new File(filename));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
java.security.cert.X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
System.out.println("Certificate Information: ");
System.out.println(cert.getSubjectDN().toString());
jks.setCertificateEntry(cert.getSubjectDN().toString(), cert);

//Initialize the keymanager and trustmanager and add them to the SSL context
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "123".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(jks);

Есть ли какая-то цепочка сертификатов, которую мне нужно создать здесь?
У меня также был p12 с этими компонентами, и после использования довольно похожего кода я добавил закрытый ключ в диспетчер ключей и корневой сертификат из p12 в диспетчер доверия. Я мог заставить это работать. Но теперь мне нужно заставить его работать без p12.

РЕДАКТИРОВАТЬ: Запрошена трассировка стека. Надеюсь, этого должно хватить. (ПРИМЕЧАНИЕ: я замаскировал имена файлов)

Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1720)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
at client.abc2.openSocketConnection(abc2.java:33)
at client.abc1.runClient(abc1.java:63)
at screens.app.abc.validateLogin(abc.java:197)
... 32 more

person highflyer    schedule 03.08.2012    source источник


Ответы (3)


Вам также необходимо добавить корневой сертификат в хранилище ключей.

person user207421    schedule 03.08.2012
comment
Спасибо еще раз. Я добавил клиентский сертификат и закрытый ключ в первое хранилище ключей (k1), а затем k1 в keymanagerfactory. Затем корневой сертификат для хранилища ключей (k2) и k2 для trustmanagerfactory. - person highflyer; 07.08.2012

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

System.setProperty("javax.net.ssl.keyStore", <keystorePath>));
System.setProperty("javax.net.ssl.keyStorePassword",<keystorePassword>));

Мне также понадобился этот аргумент виртуальной машины: -Djavax.net.ssl.trustStore=/app/certs/keystore.jk Подробнее см. здесь: https://stackoverflow.com/a/34311797/1308453

person Philip Rego    schedule 29.06.2017
comment
Ну конечно вы сделали. Хранилища ключей по умолчанию нет. Не ответ на этот вопрос. - person user207421; 29.03.2018

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

import java.net.*;
import java.io.*;

public class URLConnectionReader {
    public static void main(String[] args) throws Exception {
        URL google = new URL("https://www.google.com/");
        URLConnection yc = google.openConnection();
        BufferedReader in = new BufferedReader(new InputStreamReader(
                                    yc.getInputStream()));
        String inputLine;
        while ((inputLine = in.readLine()) != null) 
            System.out.println(inputLine);
        in.close();
    }
}

Обратите внимание, что URL-адрес имеет схему HTTPS, указывающую на использование SSL.

Если сертификат сервера подписан, но вы осуществляете доступ с использованием другого IP-адреса/доменного имени, отличного от указанного в сертификате, вы можете обойти проверку имени хоста следующим образом:

HostnameVerifier hv = new HostnameVerifier() {
    public boolean verify(String urlHostName,SSLSession session) {
        return true;
    }
};

HttpsURLConnection.setDefaultHostnameVerifier(hv);

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

person German    schedule 03.08.2012
comment
Он открывает соединение как обычно, и оно не работает. Он не использует HTTPS. Вам не нужен HostnameVerifier для SSL, только для HTTPS. Тот, который вы опубликовали, радикально небезопасен и должен быть отмечен или не опубликован вообще. Не ответ. - person user207421; 04.08.2012