NSURLSessionAuthChallengeUseCredential 没有帮助。如何让 iOS 信任我的服务器?
NSURLSessionAuthChallengeUseCredential does not help. How to make iOS trust my server?
我的服务器使用自签名 SSL 证书。而且 iOS 无论我做什么都不想接受他们。这是我的代码:
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential *credential))completionHandler
{
NSString* authenticationMethod = challenge.protectionSpace.authenticationMethod;
if (![authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
return;
}
SecTrustRef trust = challenge.protectionSpace.serverTrust;
CFIndex count = SecTrustGetCertificateCount(trust);
CFMutableArrayRef originalCertificates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (CFIndex i = 0; i < count; i++)
{
SecCertificateRef certRef = SecTrustGetCertificateAtIndex(trust, i);
CFArrayAppendValue(originalCertificates, certRef);
CFStringRef certSummary = SecCertificateCopySubjectSummary(certRef);
NSLog(@"CERT %ld %@", i, certSummary);
}
//SecPolicyRef policyRef = SecPolicyCreateSSL(true, CFSTR("192.168.50.80"));
SecPolicyRef policyRef = SecPolicyCreateBasicX509();
SecTrustRef newTrust;
OSStatus status = SecTrustCreateWithCertificates(originalCertificates, policyRef, & newTrust);
assert(status == noErr);
NSString* path = [[NSBundle mainBundle] pathForResource:@"no1bcCA" ofType:@"der"];
NSData* data = [NSData dataWithContentsOfFile:path];
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef) data);
assert(cert);
NSString* rootPath = [[NSBundle mainBundle] pathForResource:@"no1bcRootCA" ofType:@"der"];
NSData* rootData = [NSData dataWithContentsOfFile:rootPath];
SecCertificateRef rootCert = SecCertificateCreateWithData(NULL, (CFDataRef) rootData);
assert(rootCert);
SecTrustSetAnchorCertificates(newTrust, (CFArrayRef)@[(__bridge id)rootCert, (__bridge id)cert]);
SecTrustSetAnchorCertificatesOnly(newTrust, NO);
SecTrustResultType trustResult;
SecTrustEvaluate(newTrust, &trustResult);
if (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed)
{
NSURLCredential* credential = [NSURLCredential credentialForTrust:newTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}
else
{
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
所以 trustResult
是 kSecTrustResultUnspecified
但是在我的 NSURLSessionDataTask
的完成处理程序中我仍然收到以下错误:
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x6000003040b0>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<cert(0x7f81ef80ca00) s: sems.no1bc.local i: no1bcCA>",
"<cert(0x7f81ef80d400) s: no1bcCA i: no1bcRootCA>",
"<cert(0x7f81ef82b800) s: no1bcRootCA i: no1bcRootCA>"
), NSUnderlyingError=0x604000255030 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x6000003040b0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x7f81ef80ca00) s: sems.no1bc.local i: no1bcCA>",
"<cert(0x7f81ef80d400) s: no1bcCA i: no1bcRootCA>",
"<cert(0x7f81ef82b800) s: no1bcRootCA i: no1bcRootCA>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://192.168.50.80/pgpuniversaldesktop, NSErrorFailingURLStringKey=https://192.168.50.80/pgpuniversaldesktop, NSErrorClientCertificateStateKey=0}
我喜欢恢复建议。它说
Would you like to connect to the server anyway?
是的,我愿意,但是怎么做呢?我该怎么办?
除了我还尝试使用 ATS 的所有内容之外,这是我放入 plist 文件的内容:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>192.168.50.80</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSRequiresCertificateTransparency</key>
<false/>
</dict>
</dict>
</dict>
但这无济于事。所以,我明确地告诉 iOS: "Trust this server, trust it",但它没有。可能是什么原因?如何强制系统信任服务器?我应该如何连接到服务器?
有趣的是,如果我 运行 来自 Mac 应用程序的代码,它可以毫无问题地工作。但不适用于 iOS
在 iOS 11 中,显然您还需要为该域将 NSExceptionRequiresForwardSecrecy
设置为 false
。否则,App Transport Security 甚至不会让您的自定义证书到达您的自定义身份验证代码。这可以说是一个错误。
有关详细信息,请参阅 Apple 开发者论坛中的 this thread。
结果是服务器 运行 一些过时的 TLS。 Mac 能够处理它而 iOS 不能,所以无论如何它都不允许连接到服务器。解决方案是修复服务器
我的服务器使用自签名 SSL 证书。而且 iOS 无论我做什么都不想接受他们。这是我的代码:
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential *credential))completionHandler
{
NSString* authenticationMethod = challenge.protectionSpace.authenticationMethod;
if (![authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
return;
}
SecTrustRef trust = challenge.protectionSpace.serverTrust;
CFIndex count = SecTrustGetCertificateCount(trust);
CFMutableArrayRef originalCertificates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (CFIndex i = 0; i < count; i++)
{
SecCertificateRef certRef = SecTrustGetCertificateAtIndex(trust, i);
CFArrayAppendValue(originalCertificates, certRef);
CFStringRef certSummary = SecCertificateCopySubjectSummary(certRef);
NSLog(@"CERT %ld %@", i, certSummary);
}
//SecPolicyRef policyRef = SecPolicyCreateSSL(true, CFSTR("192.168.50.80"));
SecPolicyRef policyRef = SecPolicyCreateBasicX509();
SecTrustRef newTrust;
OSStatus status = SecTrustCreateWithCertificates(originalCertificates, policyRef, & newTrust);
assert(status == noErr);
NSString* path = [[NSBundle mainBundle] pathForResource:@"no1bcCA" ofType:@"der"];
NSData* data = [NSData dataWithContentsOfFile:path];
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef) data);
assert(cert);
NSString* rootPath = [[NSBundle mainBundle] pathForResource:@"no1bcRootCA" ofType:@"der"];
NSData* rootData = [NSData dataWithContentsOfFile:rootPath];
SecCertificateRef rootCert = SecCertificateCreateWithData(NULL, (CFDataRef) rootData);
assert(rootCert);
SecTrustSetAnchorCertificates(newTrust, (CFArrayRef)@[(__bridge id)rootCert, (__bridge id)cert]);
SecTrustSetAnchorCertificatesOnly(newTrust, NO);
SecTrustResultType trustResult;
SecTrustEvaluate(newTrust, &trustResult);
if (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed)
{
NSURLCredential* credential = [NSURLCredential credentialForTrust:newTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}
else
{
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
所以 trustResult
是 kSecTrustResultUnspecified
但是在我的 NSURLSessionDataTask
的完成处理程序中我仍然收到以下错误:
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x6000003040b0>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<cert(0x7f81ef80ca00) s: sems.no1bc.local i: no1bcCA>",
"<cert(0x7f81ef80d400) s: no1bcCA i: no1bcRootCA>",
"<cert(0x7f81ef82b800) s: no1bcRootCA i: no1bcRootCA>"
), NSUnderlyingError=0x604000255030 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x6000003040b0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x7f81ef80ca00) s: sems.no1bc.local i: no1bcCA>",
"<cert(0x7f81ef80d400) s: no1bcCA i: no1bcRootCA>",
"<cert(0x7f81ef82b800) s: no1bcRootCA i: no1bcRootCA>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://192.168.50.80/pgpuniversaldesktop, NSErrorFailingURLStringKey=https://192.168.50.80/pgpuniversaldesktop, NSErrorClientCertificateStateKey=0}
我喜欢恢复建议。它说
Would you like to connect to the server anyway?
是的,我愿意,但是怎么做呢?我该怎么办?
除了我还尝试使用 ATS 的所有内容之外,这是我放入 plist 文件的内容:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>192.168.50.80</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSRequiresCertificateTransparency</key>
<false/>
</dict>
</dict>
</dict>
但这无济于事。所以,我明确地告诉 iOS: "Trust this server, trust it",但它没有。可能是什么原因?如何强制系统信任服务器?我应该如何连接到服务器?
有趣的是,如果我 运行 来自 Mac 应用程序的代码,它可以毫无问题地工作。但不适用于 iOS
在 iOS 11 中,显然您还需要为该域将 NSExceptionRequiresForwardSecrecy
设置为 false
。否则,App Transport Security 甚至不会让您的自定义证书到达您的自定义身份验证代码。这可以说是一个错误。
有关详细信息,请参阅 Apple 开发者论坛中的 this thread。
结果是服务器 运行 一些过时的 TLS。 Mac 能够处理它而 iOS 不能,所以无论如何它都不允许连接到服务器。解决方案是修复服务器