0

我正在尝试连接到需要客户端证书的服务器。因此,浏览到此服务器时发生的正常事件流是 Web 浏览器(Safari 和 Chrome)提示用户选择证书并重试操作。

那么如何在 Cocoa 项目的嵌入式 WebView 中实现这一点呢?到目前为止,我已经确定该didFailProvisionalLoadWithError方法中引发了错误:

- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
    NSLog(@"webView:didFailProvisionalLoadWithError:forFrame:");
    NSLog(@"    error = %@", error);
}

错误确实是error = Error Domain=NSURLErrorDomain Code=-1206 UserInfo=0x1006a8030 "The server “myserver.xxx” requires a client certificate.
但是我如何显示一个对话框以便用户可以从钥匙串中选择一个证书?

4

2 回答 2

3

问题解决了。

WebView 组件的一个(已知)问题是罪魁祸首。向 Apple 开具了 DTS 支持票证并获得了解决方法。

编辑:这是 DTS 的解决方法(我不知道这是否仍然有效,因为它是 3 年前的):

Magnus 好的,我有机会看看这个,我知道发生了什么。在我们开始讨论 WebView 之前,我需要让您快速了解 NSURLConnection 使用的委托方法,这是用于实际从网络加载数据的底层 API。NSURLConnection 开始支持单个身份验证委托回调,-connection:didReceiveAuthenticationChallenge:,它将当时支持的各种身份验证质询(用户名/密码样式质询)传递给该回调。在 Mac OS X 10.6(和 iOS 3.0)中,NSURLConnection 得到了增强,以支持两种附加类型的 TLS 连接身份验证质询: o 客户端身份质询(NSURLAuthenticationMethodClientCertificate),让代理有机会为给定的 TLS 连接选择客户端身份 o 服务器信任挑战 (NSURLAuthenticationMethodServerTrust),让代理有机会覆盖给定 TLS 连接的服务器信任评估出于兼容性原因,无法通过这些挑战在所有情况下都向委托人发送,所以 NSURLConnection 引入了一个新的委托回调,-connection:canAuthenticateAgainstProtectionSpace:,它允许委托人选择加入这些挑战。* * * 现在,让我们将其带回您的应用程序。正如我所提到的,WebView 使用 NSURLConnection 并且对于每个连接都充当连接委托。它拦截身份验证质询并将其传递给其资源负载委托。这适用于老式的身份验证挑战,因为 WebView 无需做任何特别的事情即可获得挑战;但它对 TLS 连接身份验证挑战失败,因为委托必须选择这些挑战。您真正需要的是“canAuthenticateAgainstProtectionSpace”身份验证挑战的 WebView 版本。好吧,事实证明这是实际实现的。在查看 WebView 的开源代码时,我发现有一个私有委托回调,-webView:resource:canAuthenticateAgainstProtectionSpace:forDataSource:,这正是你想要的。因为代表必须选择接受这些挑战。您真正需要的是“canAuthenticateAgainstProtectionSpace”身份验证挑战的 WebView 版本。好吧,事实证明这是实际实现的。在查看 WebView 的开源代码时,我发现有一个私有委托回调,-webView:resource:canAuthenticateAgainstProtectionSpace:forDataSource:,这正是你想要的。因为代表必须选择接受这些挑战。您真正需要的是“canAuthenticateAgainstProtectionSpace”身份验证挑战的 WebView 版本。好吧,事实证明这是实际实现的。在查看 WebView 的开源代码时,我发现有一个私有委托回调,-webView:resource:canAuthenticateAgainstProtectionSpace:forDataSource:,这正是你想要的。 http://www.opensource.apple.com/source/WebKit/WebKit-7533.20.25/mac/WebView/WebResourceLoadDelegatePrivate.h 如果您实施该方法,您可以选择加入客户端身份验证质询,并根据该质询提供一个允许用户选择身份的用户界面。我在您的测试应用程序中对此进行了原型设计,它很有魅力。这是我用来获取客户端身份挑战的代码: - (BOOL)webView:(WebView *)sender resource:(id)identifier canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace forDataSource:(WebDataSource *)dataSource { NSLog(@"%@ ", [保护空间认证方法]); return [[protectionSpace authenticationMethod] isEqual:NSURLAuthenticationMethodClientCertificate]; 这是我用来响应它的代码: - (void)webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge *)挑战来自DataSource:(WebDataSource *)dataSource { NSLog(@"didReceiveAuthenticationChallenge"); NSString *authenticationMethod = [[挑战保护空间] authenticationMethod]; NSLog(@" authenticationMethod = %@", authenticationMethod);

 [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; }

显然,在一个真实的应用程序中,您需要显示一些 UI,然后,一旦用户选择了客户端身份,为其创建一个凭据 (+[NSURLCredential credentialWithIdentity:certificates:persistence:]),然后将该凭据应用于挑战(-useCredential:forAuthenticationChallenge:)。* * * 那么你从哪里开始呢?无论您做什么,您都应该针对 WebView 提交一个错误,以获取在公共标头中发布的 -webView:resource:canAuthenticateAgainstProtectionSpace:forDataSource: 委托回调。这是一个明显的,也是最烦人的遗漏。http://developer.apple.com/bugreporter/ 提交错误后,请将错误编号发送给我,以便我可以将其与此事件相关联。除此之外,前进的道路不太明确。如果您正在创建非 Mac App Store 应用程序,我的建议是您只需实现上面显示的“canAuthenticateAgainstProtectionSpace”委托回调,然后继续您的生活。OTOH,如果您正在创建一个 Mac App Store 应用程序,其中严格禁止使用私有 API(包括委托回调),那么生活会变得更加棘手。在这种情况下让我知道,我们可以讨论您的选择。分享和享受

于 2011-06-14T07:12:09.840 回答
1

设置一个WebResourceLoadDelegate并实现与身份验证挑战相关的委托方法。收到身份验证质询时将提示您,此时您可以提供要使用的证书。

ETA:以下是如何NSURLCredential从存储在以下位置的证书clientSide.p12

NSString *thePath = [[NSBundle mainBundle]
        pathForResource:@"clientside" ofType:@"p12"];
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (CFDataRef)PKCS12Data;    
SecIdentityRef identity;
SecTrustRef trust;
extractIdentityAndTrust(inPKCS12Data, &identity, &trust);

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

const void *certs[] = {certificate};
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);

NSURLCredential *credential = [NSURLCredential
        credentialWithIdentity:identity
        certificates:(NSArray*)certArray
        persistence:NSURLCredentialPersistencePermanent];

这来自另一个问题。您可能还会发现这个问题很有帮助。我通过谷歌搜索“nsurlcredential certificate”找到了这些。

于 2011-04-18T22:08:59.337 回答