2015-10-16 68 views
13

我试图使用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对象始终为空。

从这个苹果开发者链接:https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html

有这句话暗示这不应该是这样的:

通过您的流委托的事件处理程序被调用到 时表示有系统已经构建了TLS通道,从连接的另一端获得证书 链,并创建了一个信任对象 来评估它。

有关如何解决此问题的任何提示?

如何才能访问该NSStream的对象trustRef

编辑:

感谢您的答复100phole。

在试图得到这个工作,我认为这可能有一些做的问题,在我的许多尝试之一,我感动了所有的插座相关的项目成为阶级:

事情是这样的:

@interface Socket 
    CFReadStreamRef readStream; 
    CFWriteStreamRef writeStream; 

    NSInputStream *inputStream; 
    NSOutputStream *outputStream; 
@end 

但是,想出了相同的结果:(

我只恢复到上面显示的版本,因为,根据我的谷歌搜索,这似乎是一个相当常见的代码模式。

例如,从苹果开发者网站甚至这个代码使用了风格极为相似:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Streams/Articles/NetworkStreams.html#//apple_ref/doc/uid/20002277-BCIDFCDI

正如我前面提到的,我在Objective-C没有专家(远非如此),所以我可能是错的,但从我所见到的情况来看,将这些项目移到一个班级并让他们坚持下去似乎没有什么区别。

+2

您的流似乎是方法中的局部变量,这意味着它们将在方法返回时被销毁。 – l00phole

+0

只看你的代码足够长的时间来问...为什么不使用内建的NSURL会话和连接类?连接设置和身份验证握手都是为您处理的(必要时使用钩子进行插入)。 –

+0

你可以在你参考的网页上看到这样一条注释:/ *存储对输入和输出流的引用,以便它们不会消失.... * /' – Wain

回答

0

由于在这个问题上似乎有一些兴趣,我决定用一个答案更新问题,并详细解释这个问题最终如何解决。

首先是一些背景。我从以前的开发人员那里继承了这些代码,我的角色是让破碎的代码工作。

我花了很多时间用Apple iOS开发人员网页上的细节编写和重写连接代码,但似乎没有任何效果。

我终于决定要仔细看看这个功能,我继承,并错误地假定代码是工作:

[self loadClientCertificates:certificates]; 

乍一看代码看起来OK。该函数只是从文件加载证书。但仔细观察,当代码正确加载证书时,它并没有将这些证书返回给调用者!

修复该代码以便正确返回证书后,连接代码工作正常,并且SecTrustRef不再为NULL。

总结:

1)苹果的文档,同时缺乏很好的例子确实出现了准确的。

2)的原因SecTrustRef是NULL是因为没有有效的证书,可以用于连接谈判阶段发现,那是因为没有证书,其中被提供给连接API由于前面提到的编码错误。

3)如果您看到一个类似的错误,我的建议是检查,并仔细检查你的代码,因为正如所预料的,方程的iOS的侧面可以作为证明。

相关问题