ios - 使用 AFNetworking 进行 SSL 固定不起作用
问题描述
我正在尝试使用自签名证书将 SSL 固定添加到我的应用程序,但我似乎无法让它工作。我已经尝试了所有在互联网上可以找到的东西,但都没有成功,而且不是 SSL 工作原理方面的专家也无济于事。
我正在将objective-c 与最新版本的AFNetworking 一起使用。
我编写了一段非常简单的代码来测试我的 API 调用(我在这篇文章中使用了占位符 URL):
NSString *url = @"https://api.example.net/webservice";
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"example.net" ofType:@"der"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:url]];
manager.requestSerializer = [AFJSONRequestSerializer new];
manager.responseSerializer = [AFJSONResponseSerializer new];
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
[policy setAllowInvalidCertificates:YES];
[policy setValidatesDomainName:NO];
policy.pinnedCertificates = [NSSet setWithObject:certData];
manager.securityPolicy = policy;
[manager POST:url parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"SUCCESS");
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"FAILURE : %@", error.localizedDescription);
}];
每次我尝试执行此代码时,都会失败并出现以下错误:
Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “api.example.net” which could put your confidential information at risk."
我尝试为我的证书使用不同的格式(.der、.cer、...),但我仍然总是遇到同样的错误。
我尝试NSAllowsArbitraryLoads
在我的 info.plist 中使用,但没有任何变化。
为了确保我使用的是工作代码,我还从Ray Wenderlich 教程下载了示例项目,但我自己的证书仍然无效(在教程中他们使用 stackexchange 证书,这个有效)。
我已经研究这个问题好几天了,还没有找到解决办法。
相同的证书在我们的 Android 应用程序以及 Postman 上都能完美运行。
这是因为我使用自签名证书而 iOS 不喜欢它吗?我的代码或应用程序配置中是否有任何明显的遗漏?是否有特定的东西来实现服务器端以确保它与 iOS 一起工作?我必须以非常特定的格式导出我的证书吗?
欢迎任何信息。
谢谢!
解决方案
我正在查看一个旧项目,我使用自签名证书没有问题。这些只是可能有帮助的评论 - 我在这里制作它们是因为我有更多空间并且可以更好地格式化它们。
该der
版本有效。
在Info.plist
你需要类似下面的东西。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>server1.local</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
<key>server2.local</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
<key>server3.local</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
请注意您的错误消息 - 它抱怨服务器证书,因此问题可能出在 DNS 或服务器证书中。两者都需要正确,并且您使用的名称(例如server1.local
)必须与服务器的 DNS 名称以及CN
iOS 工作的证书匹配。
我将CA 和服务器证书都添加到 iOS 中的链中。
我相信这会对你有所帮助。
FWIW 我的实现没有使用 AFNetworking 但我SecTrustSetAnchorCertificates
在里面URLSession:didReceiveChallenge:completionHandler:
使用了NSURLSessionDelegate
我曾经支持正常的NSURLRequest
.
这是那段代码。
// Look to see if we can handle the challenge
- ( void ) URLSession:( NSURLSession * ) session
didReceiveChallenge:( NSURLAuthenticationChallenge * ) challenge
completionHandler:( void ( ^ ) ( NSURLSessionAuthChallengeDisposition, NSURLCredential * ) ) completionHandler
{
#ifdef DEBUG
NSLog( @"didReceiveChallenge %@ %zd", challenge.protectionSpace.authenticationMethod, ( ssize_t ) challenge.previousFailureCount );
#endif
NSURLCredential * credential = nil;
NSURLProtectionSpace * protectionSpace;
SecTrustRef trust;
int err;
// Setup
protectionSpace = challenge.protectionSpace;
trust = protectionSpace.serverTrust;
credential = [NSURLCredential credentialForTrust:trust];
if ( protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust )
{
// Build up the trust anchor using server certificates
err = SecTrustSetAnchorCertificates ( trust, ( CFArrayRef ) fhWebSupportDelegate.serverCertificates );
SecTrustResultType trustResult = 0;
if ( err == noErr )
{
SecTrustSetAnchorCertificatesOnly ( trust, true );
err = SecTrustEvaluate ( trust, & trustResult );
#ifdef DEBUG
NSLog ( @"Trust result %lu", ( unsigned long ) trustResult );
#endif
}
BOOL trusted =
( err == noErr ) &&
( ( trustResult == kSecTrustResultProceed ) || ( trustResult == kSecTrustResultUnspecified ) || ( trustResult == kSecTrustResultRecoverableTrustFailure ) );
// Return based on whether we decided to trust or not
if ( trusted )
{
#ifdef DEBUG
NSLog ( @"Trust evaluation succeeded" );
#endif
if ( completionHandler )
{
completionHandler ( NSURLSessionAuthChallengeUseCredential, credential );
}
}
else
{
#ifdef DEBUG
NSLog ( @"Trust evaluation failed" );
#endif
if ( completionHandler )
{
completionHandler ( NSURLSessionAuthChallengeCancelAuthenticationChallenge, credential );
}
}
}
else if ( completionHandler )
{
completionHandler ( NSURLSessionAuthChallengePerformDefaultHandling, nil );
}
}
这里fhWebSupportDelegate.serverCertificates
返回一个包含 CA 以及服务器证书的数组。此外,当我向服务器授予信任时,我也非常宽容,如代码中所示。
推荐阅读
- javascript - 转换和过滤动态 JSON 返回的对象
- php - 如何在 Symfony 4 会话中设置安全和 httponly 属性
- c# - 为什么我不能粘贴复制到剪贴板的格式化文本?
- shell - 如何编写一个命令行脚本,它将遍历文本文件中的每一行并在每行末尾附加一个字符串?
- excel - 将 Excel 公式转换为 VBA - 反向查找、多个条件和 INDEX-MATCH
- c++ - 如何处理自定义输出运算符中的iomanips?
- scala - 在Scalatra中将HTTP请求重定向到HTTPS的最简洁方法是什么?
- java - 在 Spring Web 客户端中发送请求参数
- sql - CREATE DATABASE 卡在 SQL Server 2017 故障转移群集上
- javascript - 如何根据键/值将对象转换为数组?