Как исправить " java.безопасность.сертификат.CertificateException: нет альтернативных имен субъектов " ошибка?
у меня есть клиент веб-службы Java, который использует веб-службу через HTTPS.
import javax.xml.ws.Service;
@WebServiceClient(name = "ISomeService", targetNamespace = "http://tempuri.org/", wsdlLocation = "...")
public class ISomeService
extends Service
{
public ISomeService() {
super(__getWsdlLocation(), ISOMESERVICE_QNAME);
}
когда я подключаюсь к URL-адресу службы (https://AAA.BBB.CCC.DDD:9443/ISomeService
), Я получаю исключение java.security.cert.CertificateException: No subject alternative names present
.
чтобы исправить это, я сначала побежал openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt
и получил следующее содержание в файле certs.txt
:
CONNECTED(00000003)
---
Certificate chain
0 s:/CN=someSubdomain.someorganisation.com
i:/CN=someSubdomain.someorganisation.com
-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=someSubdomain.someorganisation.com
issuer=/CN=someSubdomain.someorganisation.com
---
No client certificate CA names sent
---
SSL handshake has read 489 bytes and written 236 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 512 bit
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : RC4-MD5
Session-ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Session-ID-ctx:
Master-Key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Key-Arg : None
Start Time: 1382521838
Timeout : 300 (sec)
Verify return code: 21 (unable to verify the first certificate)
---
АФАИК, теперь мне нужно
- извлечь часть
certs.txt
между-----BEGIN CERTIFICATE-----
и-----END CERTIFICATE-----
, - измените его так, чтобы имя сертификата было равно
AAA.BBB.CCC.DDD
и - затем импортируйте результат с помощью
keytool -importcert -file fileWithModifiedCertificate
(гдеfileWithModifiedCertificate
в результате операций 1 и 2).
это правильно?
если да, то как именно я могу заставить сертификат с шага 1 Работать с IP-адресом (AAA.BBB.CCC.DDD
)?
обновление 1 (23.10.2013 15: 37 MSK): в ответ аналогичный вопрос я прочитал следующее:
если вы не контролируете это сервер, используйте его имя хоста (при условии что существует по крайней мере CN, соответствующий этому имени хоста в существующем сертификат.)
что именно означает" использовать"?
9 ответов:
я исправил проблему, отключив проверки HTTPS, используя представленный подход здесь:
Я поставил следующий код в тег
ISomeService
класс:static { disableSslVerification(); } private static void disableSslVerification() { try { // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; // Install the all-trusting trust manager SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // Create all-trusting host name verifier HostnameVerifier allHostsValid = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; // Install the all-trusting host verifier HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } }
Так как я использую
https://AAA.BBB.CCC.DDD:9443/ISomeService
только для целей тестирования, это достаточно хорошее решение.
У меня та же проблема и решена с помощью этого кода. Я поставил этот код перед первым вызовом на мои веб-сервисы.
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier( new javax.net.ssl.HostnameVerifier(){ public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) { return hostname.equals("localhost"); } });
это просто и работает нормально.
здесь является исходным источником.
проверка удостоверения сертификата выполняется в соответствии с запросами клиента.
когда ваш клиент использует
https://xxx.xxx.xxx.xxx/something
(гдеxxx.xxx.xxx.xxx
- это IP-адрес), Идентификатор сертификата проверяется по этому IP-адресу (теоретически, только с использованием расширения IP SAN).если Ваш сертификат не имеет IP SAN, но DNS SANs (или если нет DNS SAN, общее имя в теме DN), вы можете заставить это работать, заставив вашего клиента использовать URL с этим именем хоста вместо этого (или a имя хоста, для которого сертификат будет действителен, если существует несколько возможных значений). Например, если у сертификата есть имя
www.example.com
используйтеhttps://www.example.com/something
.конечно, вам понадобится это имя хоста для разрешения на этот IP-адрес.
кроме того, если есть какие-либо DNS SANs, CN в DN субъекта будет проигнорирован, поэтому используйте имя, которое соответствует одному из DNS SANs в этом случае.
чтобы импортировать сертификат:
- извлечь сертификат с сервера, например,
openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt
Это будет извлекать сертификаты в формате PEM.- преобразуйте сертификат в формат DER, так как это то, что ожидает keytool, например
openssl x509 -in certs.txt -out certs.der -outform DER
- теперь вы хотите импортировать этот сертификат в системный файл 'cacert' по умолчанию. Найдите системный файл по умолчанию 'cacerts' для вашей установки Java. Взгляните на как получить расположение cacerts java по умолчанию установка?
- импорт сертификатов в этот файл cacerts:
sudo keytool -importcert -file certs.der -keystore <path-to-cacerts>
пароль cacerts по умолчанию - "changeit".Если сертификат выдается для полного доменного имени, и вы пытаетесь подключиться по IP-адресу в своем коде Java, то это, вероятно, должно быть исправлено в вашем коде, а не возиться с самим сертификатом. Измените код для подключения по полному доменному имени. Если полное доменное имя не может быть разрешено на компьютере разработчика, просто добавьте его в файл hosts или настройте компьютер с помощью DNS сервер, который может разрешить это полное доменное имя.
моя проблема с получением этой ошибки была решена с помощью полного URL "qatest.ourCompany.com/webService" вместо того, чтобы просто "qatest / webService". Причина заключалась в том, что наш сертификат безопасности имел подстановочный знак, т. е. "*.ourCompany.com как только я ввел полный адрес, исключение исчезло. Надеюсь, это поможет.
вы не можете отключить всю проверку ssl, и поэтому вы можете просто отключить проверку имени хоста через это, что немного менее страшно, чем альтернатива:
HttpsURLConnection.setDefaultHostnameVerifier( SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
[EDIT]
Как упоминалось conapart3
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
теперь устарел, поэтому он может быть удален в более поздней версии, поэтому вы можете быть вынуждены в будущем свернуть свой собственный, хотя я бы все равно сказал, что буду держаться подальше от любых решений, где вся проверка отключена.
я исправил эту проблему правильным образом, добавив имена alt субъекта в сертификат, а не внося какие-либо изменения в код или отключая SSL, в отличие от того, что предлагают здесь другие ответы. Если вы ясно видите, что исключение говорит ,что "Suject alt names отсутствуют", поэтому правильный способ должен добавить их
пожалуйста, посмотрите на это ссылке, чтобы понять шаг за шагом.
вышеуказанная ошибка означает, что в вашем файле JKS отсутствует необходимый домен, на котором вы находитесь попытка получить доступ к приложению.Вам нужно будет использовать Open SSL и ключевой инструмент для добавления нескольких доменов
- скопируйте openssl.cnf в текущий каталог
echo '[ subject_alt_name ]' >> openssl.cnf
echo 'subjectAltName = DNS:example.mydomain1.com, DNS:example.mydomain2.com, DNS:example.mydomain3.com, DNS: localhost'>> openssl.cnf
openssl req -x509 -nodes -newkey rsa:2048 -config openssl.cnf -extensions subject_alt_name -keyout private.key -out self-signed.pem -subj '/C=gb/ST=edinburgh/L=edinburgh/O=mygroup/OU=servicing/CN=www.example.com/emailAddress=postmaster@example.com' -days 365
экспорт открытого ключа (.PEM) файл в формате PKS12. Это предложит вам ввести пароль
openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in self-signed.pem -inkey private.key -name myalias -out keystore.p12
создать.JKS из самозаверяющего PEM (Хранилище ключей)
keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
создайте сертификат из хранилища ключей или JKS-файла
keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
, поскольку вышеуказанный сертификат является самоподписанным и не подтверждена центром сертификации, он должен быть добавлен в хранилище доверенных сертификатов(cacerts файл в ниже для Mac, для Windows, узнайте, где установлен JDK является.)
sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
оригинальный ответ написал по этой ссылке здесь.
Я решил проблему следующим образом.
1. Создание класса . Класс имеет некоторые пустые реализации
class MyTrustManager implements X509TrustManager { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } @Override public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { // TODO Auto-generated method stub } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { // TODO Auto-generated method stub }
2. Создание метода
private static void disableSSL() { try { TrustManager[] trustAllCerts = new TrustManager[] { new MyTrustManager() }; // Install the all-trusting trust manager SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HostnameVerifier allHostsValid = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (Exception e) { e.printStackTrace(); } }
- вызовите метод disableSSL (), где генерируется исключение. Это сработало отлично.