iOS NSURLAuthenticationMethodClientCertificate 未请求与 ActiveSync 服务器

Posted

技术标签:

【中文标题】iOS NSURLAuthenticationMethodClientCertificate 未请求与 ActiveSync 服务器【英文标题】:iOS NSURLAuthenticationMethodClientCertificate not requested vs ActiveSync server 【发布时间】:2014-02-27 12:29:03 【问题描述】:

我正在尝试在我正在开发的 ActiveSync 客户端中实施证书身份验证。使用证书身份验证的代码可能有效,但到目前为止,服务器,或者更准确地说,ios 库对服务器响应的解释对我来说似乎不正确。这是我的代码:

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

    NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
    NSString *authenticationMethod = [protectionSpace authenticationMethod];

    if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate])
    
        NSURLCredential* credential = [ self buildCredentialClientCert];

        if ( credential == nil )
        
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        
        else
        
            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
        
    
    else if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    
        .... // do other stuff

问题是,即使我知道服务器支持客户端证书身份验证,当我设置断点时,authenticationMethod 总是设置为NSURLAuthenticationMethodServerTrust

原始 HTTPS 服务器响应包含以下内容:

错误代码:403 禁止。该页面需要客户端证书作为身份验证过程的一部分。如果您使用智能卡,则需要插入智能卡以选择适当的证书。否则,请联系您的服务器管理员。 (12213)

我的问题是,是什么决定了身份验证挑战是 NSURLAuthenticationMethodServerTrust 还是 NSURLAuthenticationMethodClientCertificate

【问题讨论】:

我正在使用 golang https 服务器,并且确实看到了 NSURLAuthenticationMethodClientCertificate 挑战,其代码与您完全相同。 【参考方案1】:

由于没有人回答这个问题,我最终确实找到了一个可行的解决方案,就在这里。

服务器信任不是服务器对客户端的挑战,它是客户端验证服务器提供的信任的机会。考虑到这一点,下面的代码不会验证该信任,但可以。

通常你会得到 NSURLAuthenticationMethodServerTrust,然后你会得到 NSURLAuthenticationMethodClientCertificate。这不是非此即彼。这是工作代码。

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

    NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
    NSString *authenticationMethod = [protectionSpace authenticationMethod];

    if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate] && self.accountCertKeychainRef != nil)
    
        SecIdentityRef identity = [KeychainUtilities retrieveIdentityWithPersistentRef:self.accountCertKeychainRef];

        NSURLCredential* credential = [CertificateUtilities getCredentialFromCert:identity];

        if ( credential == nil )
        
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        
        else
        
            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
        
    
    else if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    
        NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
    
    else if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodNTLM] || [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic])
    
        self.lastProtSpace = [challenge protectionSpace];
        if ([challenge previousFailureCount] > 2)
        
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        
        else
        
            [[challenge sender]  useCredential:[self buildCredential] forAuthenticationChallenge:challenge];
        

    
    else
    
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    

对于以下问题,您可以通过以下方式获取身份:

+ (SecIdentityRef)copyIdentityAndTrustWithCertData:(CFDataRef)inPKCS12Data password:(CFStringRef)keyPassword

    SecIdentityRef extractedIdentity = nil;
    OSStatus securityError = errSecSuccess;

    const void *keys[] = kSecImportExportPassphrase;
    const void *values[] = keyPassword;
    CFDictionaryRef optionsDictionary = NULL;

    optionsDictionary = CFDictionaryCreate(NULL, keys, values, (keyPassword ? 1 : 0), NULL, NULL);

    CFArrayRef items = NULL;
    securityError = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);

    if (securityError == errSecSuccess) 
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);

        // get identity from dictionary
        extractedIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
        CFRetain(extractedIdentity);
    

    if (optionsDictionary) 
        CFRelease(optionsDictionary);
    

    if (items) 
        CFRelease(items);
    

    return extractedIdentity;

对于那些感兴趣的人,这里是 getCredentialForCert:

+ (NSURLCredential *)getCredentialFromCert:(SecIdentityRef)identity

    SecCertificateRef certificateRef = NULL;
    SecIdentityCopyCertificate(identity, &certificateRef);

    NSArray *certificateArray = [[NSArray alloc] initWithObjects:(__bridge_transfer id)(certificateRef), nil];
    NSURLCredentialPersistence persistence = NSURLCredentialPersistenceForSession;

    NSURLCredential *credential = [[NSURLCredential alloc] initWithIdentity:identity
                                                               certificates:certificateArray
                                                                persistence:persistence];

    return credential;

【讨论】:

KeychainUtilitiesCertificateUtilities 是什么? 你是如何真正获得证书的身份的? @nhenrique 最初将证书保存到钥匙串时,您可以分离身份。请参阅上面的编辑答案。 我仍然不明白你是如何获得CredentialFromCert @tofutim 使用 getCredentialForCert 查看修改后的答案

以上是关于iOS NSURLAuthenticationMethodClientCertificate 未请求与 ActiveSync 服务器的主要内容,如果未能解决你的问题,请参考以下文章

IO复用阻塞IO非阻塞IO同步IO异步IO

四种IO模型‘阻塞IO/非阻塞IO/信号驱动IO/异步IO‘

5种IO模型阻塞IO和非阻塞IO同步IO和异步IO

网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

同步IO异步IO阻塞IO非阻塞IO之间的联系与区别

同步IO异步IO阻塞IO非阻塞IO之间的联系与区别