我有一个包含 CocoaHttpServer 的小应用程序。我只想为环回调用启用它,并且我希望能够仅从我的应用程序进行这些调用。不要问为什么——我需要它。
self.httpServer = [[HTTPServer alloc] init];
self.httpServer.port = HTTP_SERVER_PORT;
self.httpServer.interface = @"loopback";
self.httpServer.connectionClass = [HTTPSolFSConnection class];
NSError *error;
if([self.httpServer start:&error]) {
DDLogInfo(@"Started HTTP Server on port %hu", [self.httpServer listeningPort]);
}
else {
DDLogError(@"Error starting HTTP Server: %@", error);
}
服务器使用自定义 HTTPConnection 类,该类在isSecureServer方法中返回YES ,并使用使用自定义 CA 签名的证书。
- (BOOL)isSecureServer {
return YES;
}
- (NSArray *)sslIdentityAndCertificates {
NSArray *result = nil;
NSData *serverCertificateData = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"server" ofType:@"p12"]];
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { CFSTR("friday_internet") };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = NULL;
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) serverCertificateData, options, &items);
if (options) {
CFRelease(options);
}
if (securityError == errSecSuccess) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identity = (SecIdentityRef) CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
SecCertificateRef serverCertificate = NULL;
SecIdentityCopyCertificate(identity, &serverCertificate);
NSData *caCertificateData = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ca" ofType:@"der"]];
SecCertificateRef caCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) caCertificateData);
NSArray *anchorCertificates = @[ (__bridge id) serverCertificate, (__bridge id) caCertificate ];
SecTrustResultType trustResult;
SecTrustRef trust = (SecTrustRef) CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef) anchorCertificates);
SecTrustSetAnchorCertificatesOnly(trust, false);
SecTrustEvaluate(trust, &trustResult);
NSLog(@"SecTrustResultType: %d", trustResult);
result = @[ (__bridge id) identity, (__bridge id) caCertificate, (__bridge id) serverCertificate ];
if (serverCertificate) {
CFRelease(serverCertificate);
}
}
if (items) {
CFRelease(items);
}
return result;
}
每次我向服务器创建 NSURLConnection 时,它都会记录SecTrustResultType: 4并失败并出现错误:
Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid.
You might be connecting to a server that is pretending to be “localhost” which could
put your confidential information at risk." UserInfo=0xb93da40
{NSErrorFailingURLStringKey=https://localhost:12345/file.txt,
NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?,
NSErrorFailingURLKey=https://localhost:12345/file.txt,
NSLocalizedDescription=The certificate for this server is invalid.
You might be connecting to a server that is pretending to be “localhost” which could
put your confidential information at risk., NSUnderlyingError=0xf155310
"The certificate for this server is invalid. You might be connecting to a server
that is pretending to be “localhost” which could put your confidential information
at risk.", NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x13329720>}
我在 StackOverflow 上阅读了许多文章和许多问题,但没有帮助:
- 如何以编程方式将证书导入我的 iOS 应用程序的钥匙串并在需要时将身份传递给服务器?
- 如何使 iPhoneHTTPServer 成为安全服务器
- SecTrustEvaluate 始终使用 SecPolicyCreateSSL 返回 kSecTrustResultRecoverableTrustFailure
- 无法信任 iPhone 上的自签名证书
- https://developer.apple.com/library/ios/technotes/tn2232/_index.html#//apple_ref/doc/uid/DTS40012884-CH1-SECCUSTOMROOT
- https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
有两个“hacks”可以解决这个问题:
- 我可以实现connection:willSendRequestForAuthenticationChallenge: NSURLConnectionDelegate 的方法
- 我可以实现自定义 NSURLProtocol
- 将 CA 的证书通过电子邮件发送到设备并手动信任它
问题是我在我的应用程序中使用了与我的服务器通信的第三方库。它不使用 Foundation 框架,因此前两种方法不起作用。有没有办法以编程方式而不是手动发送证书?