无法从iOS访问https Web服务

pan*_*kaj 15 https ios swift tls1.2

我正在尝试访问https协议上提供的Web服务.最初我收到以下错误:

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>
Run Code Online (Sandbox Code Playgroud)

但是现在我在connectionDidFinishLoading委托方法中得到以下html作为响应:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

我正在使用以下来与服务器建立信任:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

更新1

服务器日志显示以下错误:

通过SNI提供的主机名xx.xx.xxx.xx和通过HTTP提供的主机名my_secured_host_name是不同的

如何在SNI中添加主机名?

更新2

我已经从info.plist中删除了密钥,因为该服务已经是https

更新3

当我尝试使用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>
Run Code Online (Sandbox Code Playgroud)

仍然有以下错误:

NSURLSession/NSURLConnection HTTP加载失败(kCFStreamErrorDomainSSL,-9802)errorAn发生SSL错误,无法建立与服务器的安全连接.

mag*_*gma 1

除了其他答案和评论中已经解决的 ATS 问题之外,您似乎正在尝试通过其 IP 地址连接到 SSL 服务器。\n以下参考可能有用(我逐字引用Apple 的 iOS 开发者库):

\n\n
\n

要覆盖主机名(允许一个特定站点的证书适用于另一个特定站点,或者允许证书在您通过 IP 地址连接到主机时起作用),您必须替换该策略信任策略用于确定如何解释证书的对象。为此,请首先为所需主机名创建一个新的 TLS 策略对象。然后创建一个包含该策略的数组。\n 最后,告诉信任对象使用该数组来评估未来的信任。

\n
\n\n
SecTrustRef changeHostForTrust(SecTrustRef trust)\n{\n        CFMutableArrayRef newTrustPolicies = CFArrayCreateMutable(\n                kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);\n\n        SecPolicyRef sslPolicy = SecPolicyCreateSSL(true, CFSTR("www.example.com"));\n\n        CFArrayAppendValue(newTrustPolicies, sslPolicy);\n\n#ifdef MAC_BACKWARDS_COMPATIBILITY\n        /* This technique works in OS X (v10.5 and later) */\n\n        SecTrustSetPolicies(trust, newTrustPolicies);\n        CFRelease(oldTrustPolicies);\n\n        return trust;\n#else\n        /* This technique works in iOS 2 and later, or\n           OS X v10.7 and later */\n\n        CFMutableArrayRef certificates = CFArrayCreateMutable(\n                kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);\n\n        /* Copy the certificates from the original trust object */\n        CFIndex count = SecTrustGetCertificateCount(trust);\n        CFIndex i=0;\n        for (i = 0; i < count; i++) {\n                SecCertificateRef item = SecTrustGetCertificateAtIndex(trust, i);\n                CFArrayAppendValue(certificates, item);\n        }\n\n        /* Create a new trust object */\n        SecTrustRef newtrust = NULL;\n        if (SecTrustCreateWithCertificates(certificates, newTrustPolicies, &newtrust) != errSecSuccess) {\n                /* Probably a good spot to log something. */\n\n                return NULL;\n        }\n\n        return newtrust;\n#endif\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

来源:iOS 开发者库 \xe2\x80\x94 正确覆盖 TLS 链验证 \xe2\x80\x94 操作信任对象

\n\n

请注意,在同一页面上,您可以找到另一个处理自签名 SSL 证书的代码片段(如果您正在处理此类证书)。

\n\n

要在 Swift 项目中使用此函数,请向您的项目添加一个新的C 文件(File.. New.. File.. iOS/Source/C_File),例如mysectrust.c以及相应的标头mysectrust.h如果 XCode 要求您创建桥接标头,说是):

\n\n

mysectrust.h

\n\n
#ifndef mysectrust_h\n#define mysectrust_h\n\n#include <Security/Security.h>\n\nSecTrustRef changeHostForTrust(SecTrustRef trust);\n\n#endif /* mysectrust_h */\n
Run Code Online (Sandbox Code Playgroud)\n\n

mysectrust.c

\n\n
#include "mysectrust.h"\n\nSecTrustRef changeHostForTrust(SecTrustRef trust)\n{\n    CFMutableArrayRef newTrustPolicies = CFArrayCreateMutable(\n                                                              kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);\n\n    SecPolicyRef sslPolicy = SecPolicyCreateSSL(true, CFSTR("www.example.com"));\n\n    CFArrayAppendValue(newTrustPolicies, sslPolicy);\n\n#ifdef MAC_BACKWARDS_COMPATIBILITY\n    /* This technique works in OS X (v10.5 and later) */\n\n    SecTrustSetPolicies(trust, newTrustPolicies);\n    CFRelease(oldTrustPolicies);\n\n    return trust;\n#else\n    /* This technique works in iOS 2 and later, or\n     OS X v10.7 and later */\n\n    CFMutableArrayRef certificates = CFArrayCreateMutable(\n                                                          kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);\n\n    /* Copy the certificates from the original trust object */\n    CFIndex count = SecTrustGetCertificateCount(trust);\n    CFIndex i=0;\n    for (i = 0; i < count; i++) {\n        SecCertificateRef item = SecTrustGetCertificateAtIndex(trust, i);\n        CFArrayAppendValue(certificates, item);\n    }\n\n    /* Create a new trust object */\n    SecTrustRef newtrust = NULL;\n    if (SecTrustCreateWithCertificates(certificates, newTrustPolicies, &newtrust) != errSecSuccess) {\n        /* Probably a good spot to log something. */\n\n        return NULL;\n    }\n\n    return newtrust;\n#endif\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

当然,将www.example.com上面的代码替换为您的主机名。

\n\n

然后,在 Xcode 项目中找到桥接标头,projectname-Bridging-Header.h并附加以下行:

\n\n
#import "mysectrust.h"\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在你可以从 Swift 调用这个函数,例如:

\n\n
func whatever(trust: SecTrustRef){\n\n    let newTrust = changeHostForTrust(trust) // call to C function\n    ...\n}   \n
Run Code Online (Sandbox Code Playgroud)\n