使用 AFNetworking 进行 SSL 固定不起作用

Posted

技术标签:

【中文标题】使用 AFNetworking 进行 SSL 固定不起作用【英文标题】:SSL Pinning with AFNetworking doesn't work 【发布时间】:2020-11-13 17:22:05 【问题描述】:

我正在尝试使用自签名证书将 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、...),但我仍然总是遇到同样的错误。

我尝试在 info.plist 中使用 NSAllowsArbitraryLoads,但没有任何变化。

为了确保我使用的是工作代码,我还从Ray Wenderlich tutorial 下载了示例项目,但我自己的证书仍然无效(在教程中他们使用 stackexchange 证书,这个有效)。

我研究这个问题好几天了,还没有找到解决办法。

相同的证书在我们的 android 应用以及 Postman 上完美运行。

这是因为我使用自签名证书而 ios 不喜欢它吗? 我的代码或应用程序配置中是否有任何明显的遗漏? 是否有特定的东西来实现服务器端以确保它与 iOS 一起工作? 我必须以非常特定的格式导出我的证书吗?

欢迎提供任何信息。

谢谢!

【问题讨论】:

【参考方案1】:

我正在查看一个旧项目,我在该项目中使用自签名证书没有问题。这些只是可能有帮助的 cmets - 我在这里制作它们是因为我有更多空间并且可以更好地格式化它们。

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,但我在 URLSession:didReceiveChallenge:completionHandler: 中使用了 SecTrustSetAnchorCertificatesNSURLSessionDelegate 中,我曾经支持普通的 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 和服务器证书的数组。此外,我在授予服务器信任时也非常宽容,如代码所示。

【讨论】:

非常感谢!我会调查一下,看看我能不能让它工作

以上是关于使用 AFNetworking 进行 SSL 固定不起作用的主要内容,如果未能解决你的问题,请参考以下文章

使用 AFNetworking 进行 SSL 固定不起作用

使用 AFNetworking 进行证书/SSL 固定 - 如果孩子受信任,如何信任所有父母证书?

如何允许用户在 iOS 中使用 AFNetworking 信任和固定自签名 SSL 证书

快速 afnetworking ssl 固定

iOS SSL Pinning 没有使用 AFNetworking 的证书?

SSL pinning - 在 AFSecurityPolicy 中设置固定公钥而不是固定证书