Как исправить ошибку "java.security.cert.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)
---
AFAIK, теперь мне нужно
- извлеките часть
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, соответствующий этому имени хоста в существующем CERT).
Что именно означает "использование"?
Ответы
Ответ 1
Я исправил проблему, отключив проверки 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
только для тестирования, это достаточно хорошее решение.
Ответ 2
У меня та же проблема и решена с помощью этого кода.
Я помещаю этот код перед первым вызовом своих веб-сервисов.
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
return hostname.equals("localhost");
}
});
Это просто и прекрасно работает.
Здесь является исходным источником.
Ответ 3
Проверка идентификатора сертификата выполняется против того, что клиент запрашивает.
Когда ваш клиент использует https://xxx.xxx.xxx.xxx/something
(где xxx.xxx.xxx.xxx
- IP-адрес), идентификатор сертификата проверяется на этот IP-адрес (теоретически, только с использованием расширения IP SAN).
Если ваш сертификат не имеет IP SAN, но DNS SAN (или если DNS SAN, общее имя в DN темы), вы можете заставить это работать, заставляя вместо этого использовать URL-адрес с этим именем узла (или имя хоста, для которого сертификат будет действительным, если имеется несколько возможных значений). Например, если у вас есть имя для www.example.com
, используйте https://www.example.com/something
.
Конечно, для этого IP-адреса потребуется имя хоста.
Кроме того, если есть какие-либо DNS-SAN, CN в DN субъекта будет игнорироваться, поэтому в этом случае используйте имя, соответствующее одному из DNS SAN.
Ответ 4
Это старый вопрос, но у меня была такая же проблема при переходе с JDK 1.8.0_144 на jdk 1.8.0_191
Мы нашли подсказку в журнале изменений:
Изменения
мы добавили следующее дополнительное системное свойство, которое помогло в нашем случае решить эту проблему:
-Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true
Ответ 5
Чтобы импортировать сертификат:
- Извлеките сертификат с сервера, например.
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-коде, то это, вероятно, должно быть исправлено в вашем коде, а не возиться с самим сертификатом. Измените свой код для подключения по FQDN. Если FQDN не разрешимо на вашем компьютере-разработчике, просто добавьте его в файл hosts или настройте свой компьютер с DNS-сервером, который может разрешить это полное доменное имя.
Ответ 6
Я исправил эту проблему правильным образом, добавив альтернативные имена субъекта в сертификат, вместо того, чтобы вносить какие-либо изменения в код или отключать 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/[email protected]' -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
Создайте a.JKS из самоподписанного PEM (Keystore)
keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
Сгенерируйте сертификат из хранилища ключей или JKS файла
keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
Поскольку вышеуказанный сертификат является самоподписанным и не проверен CA, его необходимо добавить в Truststore (файл 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
Оригинальный ответ размещен по этой ссылке здесь.
Ответ 7
моя проблема с получением этой ошибки была решена с использованием полного URL-адреса "qatest.ourCompany.com/webService" вместо "qatest/webService". Причина заключалась в том, что наш сертификат безопасности имел подстановочный знак, т.е. "*.ourCompany.com". Как только я включил полный адрес, исключение исчезло. Надеюсь, это поможет.
Ответ 8
Возможно, вы не захотите отключать все проверки подлинности ssl, поэтому вы можете просто отключить проверку hostName, что немного менее страшно, чем альтернатива:
HttpsURLConnection.setDefaultHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
[РЕДАКТИРОВАТЬ]
Как упомянуто в conapart3, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
теперь устарела, поэтому его можно удалить в более поздней версии, так что в будущем вы можете быть вынуждены откатить свою собственную, хотя я все же сказал бы, что я буду держаться подальше от любых решений, где вся проверка выполняется выключен.
Ответ 9
Ответили уже на /fooobar.com/....
Это не удается, поскольку ни общее имя сертификата (CN
в Subject
сертификации), ни какое-либо из альтернативных имен (Subject Alternative Name
в сертификате) не совпадают с целевым именем хоста или IP-адресом.
Например, из JVM при попытке подключиться к IP-адресу (WW.XX.YY.ZZ
), а не к DNS-имени (/fooobar.com/...), соединение HTTPS не будет установлено, поскольку сертификат, хранящийся в jace truststore cacerts
ожидает, что общее имя (или альтернативное имя сертификата, например stackexchange.com или *.stackoverflow.com и т.д.) будет соответствовать целевому адресу.
Пожалуйста, проверьте: https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#HostnameVerifier
HttpsURLConnection urlConnection = (HttpsURLConnection) new URL("https://WW.XX.YY.ZZ/api/verify").openConnection();
urlConnection.setSSLSocketFactory(socketFactory());
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("GET");
urlConnection.setUseCaches(false);
urlConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession sslSession) {
return true;
}
});
urlConnection.getOutputStream();
Выше передан реализованный объект HostnameVerifier
который всегда возвращает true
:
new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession sslSession) {
return true;
}
}
Ответ 10
Я получил этот вопрос после того, как получил это же сообщение об ошибке. Однако в моем случае у нас было два URL с разными поддоменами (http://example1.xxx.com/someservice и http://example2.yyy.com/someservice), которые были направлены на один и тот же сервер. Этот сервер имел только один шаблонный сертификат для домена *.xxx.com. При использовании сервиса через второй домен найденный сертификат (*.xxx.com) не совпадает с запрошенным доменом (*.yyy.com), и возникает ошибка.
В этом случае мы не должны пытаться исправить такое сообщение об ошибке путем снижения безопасности SSL, но должны проверить сервер и сертификаты на нем.
Ответ 11
Работает нормально на Java 1.8 Спасибо
Ответ 12
public class RESTfulClientSSL {
static TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
}};
public class NullHostNameVerifier implements HostnameVerifier {
/*
* (non-Javadoc)
*
* @see javax.net.ssl.HostnameVerifier#verify(java.lang.String,
* javax.net.ssl.SSLSession)
*/
@Override
public boolean verify(String arg0, SSLSession arg1) {
// TODO Auto-generated method stub
return true;
}
}
public static void main(String[] args) {
HttpURLConnection connection = null;
try {
HttpsURLConnection.setDefaultHostnameVerifier(new RESTfulwalkthroughCer().new NullHostNameVerifier());
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
String uriString = "https://172.20.20.12:9443/rest/hr/exposed/service";
URL url = new URL(uriString);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
//connection.setRequestMethod("POST");
BASE64Encoder encoder = new BASE64Encoder();
String username = "admin";
String password = "admin";
String encodedCredential = encoder.encode((username + ":" + password).getBytes());
connection.setRequestProperty("Authorization", "Basic " + encodedCredential);
connection.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
StringBuffer stringBuffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null) {
stringBuffer.append(line);
}
String content = stringBuffer.toString();
System.out.println(content);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}
Ответ 13
Я собирался через 2 пути SSL в Springboot. Я сделал все правильные настройки службы сервера tomcat и вызывающей службы RestTemplate. но я получаю сообщение об ошибке как "java.security.cert.CertificateException: нет альтернативных имен субъектов"
Обнаружив решения, я обнаружил, что JVM нужен этот сертификат, иначе он выдаст ошибку подтверждения связи.
Теперь, как добавить это в JVM.
перейдите в файл jre/lib/security/cacerts. нам нужно добавить наш файл сертификата сервера в этот файл cacerts jvm.
Команда для добавления сертификата сервера в файл cacerts через командную строку в Windows.
C:\Program Files\Java\jdk1.8.0_191\jre\lib\security>keytool -import -noprompt -trustcacerts -alias sslserver -file E:\spring_cloud_sachin\ssl_keys\sslserver.cer -keystore cacerts -storepass changeit
Проверьте, установлен сертификат сервера или нет:
C:\Program Files\Java\jdk1.8.0_191\jre\lib\security>keytool -list -keystore cacerts
Вы можете увидеть список установленных сертификатов:
Больше подробностей: https://sachin4java.blogspot.com/2019/08/javasecuritycertcertificateexception-no.html
Ответ 14
добавьте запись хоста с IP-адресом, соответствующим CN, в сертификате
CN = someSubdomain.someorganisation.com
Теперь обновите IP-адрес с именем CN, где вы пытаетесь получить доступ к URL.
Это сработало для меня.
Ответ 15
Добавьте свой IP-адрес в файл hosts. который находится в папке C:\Windows\System32\drivers\etc.
Также добавьте IP и доменное имя IP-адреса.
пример:
aaa.bbb.ccc.ddd [email protected]
Ответ 16
Я решил проблему следующим образом.
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(), в котором генерируется исключение. Он работал нормально.