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

АФАИК, теперь мне нужно

  1. извлечь часть certs.txt между -----BEGIN CERTIFICATE----- и -----END CERTIFICATE-----,
  2. измените его так, чтобы имя сертификата было равно AAA.BBB.CCC.DDD и
  3. затем импортируйте результат с помощью keytool -importcert -file fileWithModifiedCertificate (где fileWithModifiedCertificate в результате операций 1 и 2).

это правильно?

если да, то как именно я могу заставить сертификат с шага 1 Работать с IP-адресом (AAA.BBB.CCC.DDD)?

обновление 1 (23.10.2013 15: 37 MSK): в ответ аналогичный вопрос я прочитал следующее:

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

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

9 66

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 в этом случае.

чтобы импортировать сертификат:

  1. извлечь сертификат с сервера, например,openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt Это будет извлекать сертификаты в формате PEM.
  2. преобразуйте сертификат в формат DER, так как это то, что ожидает keytool, например openssl x509 -in certs.txt -out certs.der -outform DER
  3. теперь вы хотите импортировать этот сертификат в системный файл 'cacert' по умолчанию. Найдите системный файл по умолчанию 'cacerts' для вашей установки Java. Взгляните на как получить расположение cacerts java по умолчанию установка?
  4. импорт сертификатов в этот файл 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 и ключевой инструмент для добавления нескольких доменов

  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/emailAddress=postmaster@example.com' -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. создать.JKS из самозаверяющего PEM (Хранилище ключей)

    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. , поскольку вышеуказанный сертификат является самоподписанным и не подтверждена центром сертификации, он должен быть добавлен в хранилище доверенных сертификатов(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();
    }
}

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

добавьте свой IP-адрес в файл hosts.который находится в папке C:\Windows\System32\drivers\etc. Также добавьте IP и доменное имя IP-адреса. образец: ААА.ГЭБ.КТС.ddd abc@def.com