我试图使用TLS通过TCP/IP将iOS应用程序连接到Windows C#服务器。iOS SecTrustRef始终为NULL
的TLS连接,使用从使用makecert效用的不可信CA根认证证书创建不受信任的证书。
为了测试这些证书,我创建了一个简单的C#客户端,并使用这些证书可以连接到服务器并与之通信。
我不擅长iOS开发,但我还是设法找到我连接到服务器的一些代码,如下所示:
-(bool)CreateAndConnect:(NSString *) remoteHost withPort:(NSInteger) serverPort
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(remoteHost),
serverPort, &readStream, &writeStream);
CFReadStreamSetProperty(readStream, kCFStreamPropertySocketSecurityLevel,
kCFStreamSocketSecurityLevelNegotiatedSSL);
NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
// load certificate from servers exported p12 file
NSArray *certificates = [[NSArray alloc] init];
[self loadClientCertificates:certificates];
NSDictionary *sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain,
certificates,(id)kCFStreamSSLCertificates,
nil];
[inputStream setProperty:sslSettings forKey:(__bridge NSString *)kCFStreamPropertySSLSettings];
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
return true;
}
的代码也似乎做某种形式的TLS协商,因为如果未将p12证书作为NSStream设置的一部分提供,C#服务器会拒绝连接。
所以看起来像TLS协商的第一阶段正在工作。
为了验证服务器证书,我有这个功能,这得到由NSStream代表的NSStreamEventHasSpaceAvailable事件称为:
// return YES if certificate verification is successful, otherwise NO
-(BOOL) VerifyCertificate:(NSStream *)stream
{
NSData *trustedCertData = nil;
BOOL result = NO;
SecTrustRef trustRef = NULL;
NSString *root_certificate_name = @"reference_cert";
NSString *root_certificate_extension = @"der";
/* Load reference cetificate */
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
trustedCertData = [NSData dataWithContentsOfFile:[bundle pathForResource: root_certificate_name ofType: root_certificate_extension]];
/* get trust object */
/* !!!!! error is here as trustRef is NULL !!!! */
trustRef = (__bridge SecTrustRef)[stream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust];
/* loacate the reference certificate */
NSInteger numCerts = SecTrustGetCertificateCount(trustRef);
for (NSInteger i = 0; i < numCerts; i++) {
SecCertificateRef secCertRef = SecTrustGetCertificateAtIndex(trustRef, i);
NSData *certData = CFBridgingRelease(SecCertificateCopyData(secCertRef));
if ([trustedCertData isEqualToData: certData]) {
result = YES;
break;
}
}
return result;
}
现在的问题是,无论我怎么努力,该trustRef对象始终为空。
有这句话暗示这不应该是这样的:
通过您的流委托的事件处理程序被调用到 时表示有系统已经构建了TLS通道,从连接的另一端获得证书 链,并创建了一个信任对象 来评估它。
有关如何解决此问题的任何提示?
如何才能访问该NSStream的对象trustRef?
编辑:
感谢您的答复100phole。
在试图得到这个工作,我认为这可能有一些做的问题,在我的许多尝试之一,我感动了所有的插座相关的项目成为阶级:
事情是这样的:
@interface Socket
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
NSInputStream *inputStream;
NSOutputStream *outputStream;
@end
但是,想出了相同的结果:(
我只恢复到上面显示的版本,因为,根据我的谷歌搜索,这似乎是一个相当常见的代码模式。
例如,从苹果开发者网站甚至这个代码使用了风格极为相似:
正如我前面提到的,我在Objective-C没有专家(远非如此),所以我可能是错的,但从我所见到的情况来看,将这些项目移到一个班级并让他们坚持下去似乎没有什么区别。
您的流似乎是方法中的局部变量,这意味着它们将在方法返回时被销毁。 – l00phole
只看你的代码足够长的时间来问...为什么不使用内建的NSURL会话和连接类?连接设置和身份验证握手都是为您处理的(必要时使用钩子进行插入)。 –
你可以在你参考的网页上看到这样一条注释:/ *存储对输入和输出流的引用,以便它们不会消失.... * /' – Wain