无法从iOS访问https服务

我正在尝试访问https协议可用的Web服务.最初我得到以下错误:

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL,-9802)
errorAn SSL error has occurred and a secure connection to the server cannot be made.

我通过在我的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>

但是现在我正在连接到DIDFinishLoading代理方法中作为html响应:

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:
服务器日志显示以下错误:

Hostname xx.xx.xxx.xx provided via SNI and hostname my_secured_host_name provided via HTTP are different

如何在SNI中添加主机名?

UPDATE2:
由于服务已经是https,所以我已经从info.plist中删除了通过http的密钥

UPDATE3:
当我尝试使用openssl as

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

但我得到以下错误:

CONNECTED(00000003) 8012:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:/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 load failed (kCFStreamErrorDomainSSL,-9802)
errorAn SSL error has occurred and a secure connection to the server cannot be made.

问候
潘卡

解决方法

在其他答案和评论中已经解决的ATS问题之上,您似乎试图通过其IP地址连接到SSL服务器.
以下参考可能是有用的(我从苹果的iOS开发库中逐字引用):

To override the hostname (to allow a certificate for one specific site
to work for another specific site,or to allow a certificate to work
when you connected to a host by its IP address
),you must replace
the policy object that the trust policy uses to determine how to
interpret the certificate. To do this,first create a new TLS policy
object for the desired hostname. Then create an array containing that
policy. Finally,tell the trust object to use that array for future
evaluation of trust.

SecTrustRef changeHostForTrust(SecTrustRef trust)
{
        CFMutableArrayRef newTrustPolicies = CFArrayCreateMutable(
                kCFAllocatorDefault,&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,&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 Developer Library — Overriding TLS Chain Validation Correctly — Manipulating Trust Objects

请注意,在同一页面上,您可以找到处理自签名SSL证书的其他代码段,以防您处理此类证书.

要在Swift项目中使用此功能,请将新的C文件添加到您的项目(File .. New .. File .. 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,&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,&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,&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
    ...
}

以上是来客网为你收集整理的无法从iOS访问https服务全部内容,希望文章能够帮你解决无法从iOS访问https服务所遇到的程序开发问题。

如果觉得来客网网站内容还不错,欢迎将来客网网站推荐给程序员好友。