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

Не удалось получить доступ к веб-сервису https из iOS

Я пытаюсь получить доступ к веб-службе, доступной по протоколу https. Первоначально я получал следующую ошибку:

NSURLSession/NSURLConnection Ошибка загрузки HTTP (kCFStreamErrorDomainSSL, -9802) errorAn Ошибка SSL и не удалось создать безопасное соединение с сервером.

Я исправил это, добавив следующее в мой info.plist:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>NSExceptionDomains</key>
    <dict>
        <key>xx.xx.xxx.xxx</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>

Но теперь я получаю следующее как html в ответе в connectionDidFinishLoading методом делегирования:

ошибка 400, неверный запрос

Неверный запрос

Ваш браузер отправил запрос, который этот сервер не мог понять.

Я использую следующую команду для настройки доверия с сервером:

func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool{
    return true
}
func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge){
    print("willSendRequestForAuthenticationChallenge")

    let protectionSpace:NSURLProtectionSpace = challenge.protectionSpace
    let sender: NSURLAuthenticationChallengeSender? = challenge.sender

    if(protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust){
        let trust:SecTrustRef = challenge.protectionSpace.serverTrust!
        let credential:NSURLCredential = NSURLCredential.init(forTrust: trust)
        sender?.useCredential(credential, forAuthenticationChallenge: challenge)
    }
    else{
        sender?.performDefaultHandlingForAuthenticationChallenge!(challenge)
    }
}

Кто-нибудь может помочь мне выяснить проблему?

Update1:
В журналах сервера отображается следующая ошибка:

Имя хоста xx.xx.xxx.xx, предоставленное через SNI и имя хоста my_secured_host_name, предоставленное через HTTP, отличается

Как добавить имя хоста в SNI?

Update2: Я удалил ключ, передав http из info.plist, поскольку служба уже https

Update3: Когда я попытался с openssl как

openssl s_client -showcerts -connect xx.xx.xxx.xxx:443

но я получаю следующую ошибку:

CONNECTED (00000003) 8012: ошибка: 140790E5: процедуры SSL: SSL23_WRITE: сбой рукопожатия ssl:/SourceCache/OpenSSL098/OpenSSL098-52.40.1/src/ssl/s23_lib.c: 185

Update4: Изменен Info.plist следующим образом:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>xx.xx.xxx.xxx</key>
        <dict>
            <key>NSExceptionRequiresForwardSecrecy</key>
            <false/>
            <key>NSIncludesSubdomains</key>
            <true/>
        </dict>
    </dict>
</dict>

По-прежнему появляется следующая ошибка:

NSURLSession/NSURLConnection Ошибка загрузки HTTP (kCFStreamErrorDomainSSL, -9802) errorAn Ошибка SSL и не удалось создать безопасное соединение с сервером.

С уважением
Панкай

4b9b3361

Ответ 1

Помимо проблем с ATS, которые уже рассмотрены в других ответах и ​​комментариях, кажется, что вы пытаетесь подключиться к SSL-серверу по его IP-адресу. Следующая ссылка может быть полезна (я цитирую стенографию из Библиотеки разработчиков Apple iOS):

Чтобы переопределить имя хоста (чтобы разрешить сертификат для одного конкретного сайта для работы на другом конкретном сайте или , чтобы разрешить работу сертификата когда вы подключились к хосту по его IP-адресу), вы должны заменить объект политики, используемый политикой доверия для определения того, как интерпретировать сертификат. Для этого сначала создайте новую политику TLS объект для желаемого имени хоста. Затем создайте массив, содержащий политика. Наконец, скажите объекту доверия использовать этот массив для будущего оценка доверия.

SecTrustRef changeHostForTrust(SecTrustRef trust)
{
        CFMutableArrayRef newTrustPolicies = CFArrayCreateMutable(
                kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);

        SecPolicyRef sslPolicy = SecPolicyCreateSSL(true, CFSTR("www.example.com"));

        CFArrayAppendValue(newTrustPolicies, sslPolicy);

#ifdef MAC_BACKWARDS_COMPATIBILITY
        /* This technique works in OS X (v10.5 and later) */

        SecTrustSetPolicies(trust, newTrustPolicies);
        CFRelease(oldTrustPolicies);

        return trust;
#else
        /* This technique works in iOS 2 and later, or
           OS X v10.7 and later */

        CFMutableArrayRef certificates = CFArrayCreateMutable(
                kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);

        /* Copy the certificates from the original trust object */
        CFIndex count = SecTrustGetCertificateCount(trust);
        CFIndex i=0;
        for (i = 0; i < count; i++) {
                SecCertificateRef item = SecTrustGetCertificateAtIndex(trust, i);
                CFArrayAppendValue(certificates, item);
        }

        /* Create a new trust object */
        SecTrustRef newtrust = NULL;
        if (SecTrustCreateWithCertificates(certificates, newTrustPolicies, &newtrust) != errSecSuccess) {
                /* Probably a good spot to log something. */

                return NULL;
        }

        return newtrust;
#endif
}

Источник: Библиотека разработчиков iOS - Правильно переопределить проверку правильности TLS - Управление объектами доверия

Обратите внимание, что на той же странице вы можете найти другой фрагмент кода, который имеет дело с самоподписанными сертификатами SSL, если вы имеете дело с таким сертификатом.

Чтобы использовать эту функцию в проекте Swift, добавьте в проект новый файл C (Файл.. Новый.. Файл.. iOS/Source/C_File), например. mysectrust.c и соответствующий заголовок mysectrust.h (если XCode попросит вас создать заголовок моста, скажем да):

mysectrust.h

#ifndef mysectrust_h
#define mysectrust_h

#include <Security/Security.h>

SecTrustRef changeHostForTrust(SecTrustRef trust);

#endif /* mysectrust_h */

mysectrust.c

#include "mysectrust.h"

SecTrustRef changeHostForTrust(SecTrustRef trust)
{
    CFMutableArrayRef newTrustPolicies = CFArrayCreateMutable(
                                                              kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);

    SecPolicyRef sslPolicy = SecPolicyCreateSSL(true, CFSTR("www.example.com"));

    CFArrayAppendValue(newTrustPolicies, sslPolicy);

#ifdef MAC_BACKWARDS_COMPATIBILITY
    /* This technique works in OS X (v10.5 and later) */

    SecTrustSetPolicies(trust, newTrustPolicies);
    CFRelease(oldTrustPolicies);

    return trust;
#else
    /* This technique works in iOS 2 and later, or
     OS X v10.7 and later */

    CFMutableArrayRef certificates = CFArrayCreateMutable(
                                                          kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);

    /* Copy the certificates from the original trust object */
    CFIndex count = SecTrustGetCertificateCount(trust);
    CFIndex i=0;
    for (i = 0; i < count; i++) {
        SecCertificateRef item = SecTrustGetCertificateAtIndex(trust, i);
        CFArrayAppendValue(certificates, item);
    }

    /* Create a new trust object */
    SecTrustRef newtrust = NULL;
    if (SecTrustCreateWithCertificates(certificates, newTrustPolicies, &newtrust) != errSecSuccess) {
        /* Probably a good spot to log something. */

        return NULL;
    }

    return newtrust;
#endif
}

Конечно, замените www.example.com в приведенном выше коде своим именем хоста.

Затем найдите заголовок моста в проекте Xcode projectname-Bridging-Header.h и добавьте следующую строку:

#import "mysectrust.h"

Теперь вы можете просто вызвать эту функцию из Swift, например:

func whatever(trust: SecTrustRef){

    let newTrust = changeHostForTrust(trust) // call to C function
    ...
}   

Ответ 2

Если вы не сообщите нам реальный URL-адрес, вам будет сложно помочь вам

Это исправления Apple для безопасных подключений:

Это требования к безопасности приложений для приложений: сервер должен поддержка, по меньшей мере, протокола безопасности транспортного уровня (TLS) версии 1.2. Шифры подключения ограничены теми, которые обеспечивают прямую секретность (см. список шифров ниже.)

Сертификаты должны быть подписаны с использованием SHA256 или большего размера хеширования алгоритмом с 2048-битным или большим RSA-ключом или 256-битным или большей эллиптической кривой (ECC). Недействительные сертификаты приводят к жесткий отказ и отсутствие соединения. Это принятые шифры:

  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

Итак, по крайней мере один из них терпит неудачу

Если вы не уверены, какой из них и у вас есть El Capitan, просто запустите утилиту nscurl /usr/bin/nscurl --ats-diagnostics xx.xx.xxx.xxx, и она проверит все возможные ключи и значения и сообщит вам, какие из них работают.

У меня была такая же проблема, и ключи, которые работали для меня там, где (мой сертификат был подписан с SHA1 и SHA256 или выше, нужен):

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>xx.xx.xxx.xxx</key>
            <dict>
                <key>NSExceptionRequiresForwardSecrecy</key>
                <false/>
                <key>NSIncludesSubdomains</key>
                <true/>
            </dict>
        </dict>
    </dict>

Ответ 3

У меня была такая же проблема, как и вы, и я мог ее решить - в основном, после того, что было уже исследовано вами, размещено здесь, и справочная информация, предоставленная Стивен Петерсон.

Я обнаружил, что если я попробую подключиться со следующими настройками, ошибка -9802 исчезнет:

Настройки NSAppTransportSecurity

Тогда это было просто вопрос о том, какая из этих настроек предоставляет решение. Это просто вопрос о том, какой из них он снова разрывает, если удаляется.

Это может, конечно, быть другим в вашем случае.

Обратите внимание, что я указываю домен по имени, а не по числу, как указывалось ранее магмой. Также (по крайней мере, в моем случае) в решении этой проблемы не использовалось кодирование.

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

/usr/bin/nscurl --ats-diagnostics --verbose https://your-domain.com