Подтвердить что ты не робот

Как исправить ошибку "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).

Что именно означает "использование"?

4b9b3361

Ответ 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 и инструмент подсказки ключей

  1. Скопируйте файл openssl.cnf в текущий каталог
  2. echo '[ subject_alt_name ]' >> openssl.cnf
  3. echo 'subjectAltName = DNS:example.mydomain1.com, DNS:example.mydomain2.com, DNS:example.mydomain3.com, DNS: localhost'>> openssl.cnf
  4. 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
  5. Экспортируйте файл с открытым ключом (.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
    
  6. Создайте a.JKS из самоподписанного PEM (Keystore)

    keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
    
  7. Сгенерируйте сертификат из хранилища ключей или JKS файла

    keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
    
  8. Поскольку вышеуказанный сертификат является самоподписанным и не проверен 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();
    }
}

  1. Вызвать метод disableSSL(), в котором генерируется исключение. Он работал нормально.