我正在尝试访问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>
但是现在我在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>
我正在使用以下方式与服务器建立信任:
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)
}
}
服务器日志显示以下错误:
通过SNI提供的主机名xx.xx.xxx.xx和通过HTTP提供的主机名my_secured_host_name是不同的
如何在SNI中添加主机名?
由于服务已经是https
,我已从info.plist传递http删除了密钥当我尝试使用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错误,无法与服务器建立安全连接。
答案 0 :(得分:2)
我和你有同样的问题,我可以解决它 - 主要是按照你已经研究过的,在这里发布的,以及Steven Peterson提供的背景信息。
我发现如果尝试使用以下设置进行连接,则-9802错误消失了:
然后只是看看哪些设置提供了解决方案。这只是一个问题,如果被删除,找出哪一个再打破它。
当然,这可能与您的情况不同。
请注意,我确实按照名称而不是数字来指定域名,正如之前magma所指出的那样。 另外(至少在我的情况下)没有编码来解决这个问题。
解决了这个问题之后,我后来了解到确定要使用哪些设置可以实现自动化:
/usr/bin/nscurl --ats-diagnostics --verbose https://your-domain.com
答案 1 :(得分:1)
如果您没有告诉我们真实的网址,那么很难帮助您
这些是用于安全连接的Apple要求:
这些是App Transport Security要求:服务器必须 至少支持传输层安全性(TLS)协议版本1.2。 连接密码仅限于提供前向保密的密码 (参见下面的密码列表。)
必须使用SHA256或更高签名哈希对证书进行签名 算法,使用2048位或更高的RSA密钥或256位或 更大的椭圆曲线(ECC)键。无效的证书会导致a 硬故障,没有联系。这些是公认的密码:
- 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>
答案 2 :(得分:1)
除了已在其他答案和评论中解决的ATS问题之外,似乎您尝试通过其IP地址连接到SSL服务器。 以下参考可能有用(我将从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 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, 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
...
}