简体   繁体   English

iOS SecTrustRef 始终为 NULL

[英]iOS SecTrustRef Always NULL

I'm trying to connect an iOS app to a Windows C# sever using TLS over TCP/IP.我正在尝试使用 TCP/IP 上的 TLS 将 iOS 应用程序连接到 Windows C# 服务器。

The TLS connection is using untrusted certificates created from an untrusted CA root certificate using the makecert utility. TLS连接使用从使用makecert效用不可信CA根证书创建不受信任的证书

To test these certificates I created a simple C# client and using those certificates it was able to connect and communicate with the server.为了测试这些证书,我创建了一个简单的 C# 客户端,并使用这些证书来连接服务器并与之通信。

I'm not skilled at iOS development, but I did manage to find some code that connects me to the server, as follows:我不擅长 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;
}

The code also seems to do some form of TLS negotiation, as the C# server rejects the connection if the p12 certificates are not provided as part of the NSStream settings.该代码似乎还进行了某种形式的 TLS 协商,因为如果 p12 证书未作为 NSStream 设置的一部分提供,C# 服务器将拒绝连接。

So it appears like the first stage of the TLS negotiation is working.所以看起来 TLS 协商的第一阶段正在起作用。

To validate the server certificate I have this function, which gets called by the NSStream delegate on the NSStreamEventHasSpaceAvailable event:为了验证服务器证书,我有这个函数,它由 NSStreamEventHasSpaceAvailable 事件的 NSStream 委托调用:

// 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;
}

Now the problem is, no matter what I try, the trustRef object is always null.现在的问题是,无论我尝试什么, trustRef对象始终为空。

From this Apple developer link: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html来自此 Apple 开发人员链接: https : //developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html

There is this quote that suggest this should not be the case:有这句话表明情况不应该是这样:

By the time your stream delegate's event handler gets called to indicate that there is space available on the socket, the operating system has already constructed a TLS channel, obtained a certificate chain from the other end of the connection, and created a trust object to evaluate it.当您的流委托的事件处理程序被调用以指示套接字上有可用空间时,操作系统已经构建了一个 TLS 通道,从连接的另一端获取了一个证书链,并创建了一个信任对象来评估它。

Any hints on how to fix this?有关如何解决此问题的任何提示?

How can I get access to that trustRef object for the NSStream?如何访问NSStream 的 trustRef对象?

Edit:编辑:

Thanks for the reply 100phole.感谢100phole的回复。

In trying to get this to work, I thought this might have something to do with the issue and in one of my many attempts I moved all of those socket related items into a class:在尝试让它发挥作用时,我认为这可能与问题有关,在我多次尝试之一中,我将所有这些与套接字相关的项目移动到一个类中:

Something like this:像这样的东西:

@interface Socket
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    NSInputStream *inputStream;
    NSOutputStream *outputStream;
@end

But that came up with the same results :(但这得出了相同的结果:(

I only reverted back to the version shown above because, based on my Google searching, that appears to be a fairly code common pattern.我只恢复到上面显示的版本,因为根据我的谷歌搜索,这似乎是一个相当代码常见的模式。

For example, even this code from the Apple Developer site uses a very similar style:例如,即使是来自 Apple Developer 站点的这段代码也使用了非常相似的风格:

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

As I mentioned earlier, I'm no expert in Objective-C (far from it), so I might be wrong, but from what I have seen, moving those items into a class and having them persist did not seem to make any difference.正如我之前提到的,我不是 Objective-C 的专家(远非它),所以我可能是错的,但从我所看到的,将这些项目移动到一个类中并让它们持久化似乎没有任何区别.

Since there seems to be some interest in this question I decided to update the question with a answer with details on how this problem was eventually solved.由于似乎对这个问题有些兴趣,我决定用一个答案更新这个问题,详细说明这个问题最终是如何解决的。

First some background.首先介绍一下背景。 I inherited this code from a previous developer and my role was to get the broken code to work.我从以前的开发人员那里继承了这段代码,我的职责是让损坏的代码工作。

I spent a lot of time writing and re-writing the connection code using the details from the Apple iOS developer web page, but nothing seemed to work.我花了很多时间使用 Apple iOS 开发人员网页中的详细信息编写和重新编写连接代码,但似乎没有任何效果。

I finally decided to take a closer look at this function, code I had inherited and incorrectly assumed was working:我最终决定仔细研究这个函数,我继承并错误地假设它正在工作的代码:

[self loadClientCertificates:certificates];

At first glance the code looked OK.乍一看,代码看起来不错。 The function did nothing more than load certificates from file.该函数只不过是从文件加载证书。 But on closer inspection, while the code was loading the certificates correctly, it was not returning those certificates to the caller!!!但是仔细检查,虽然代码正确加载了证书,但它并没有将这些证书返回给调用者!!!

After fixing that code so that it correctly returned the certificates the connection code worked fine and the SecTrustRef was no longer NULL.修复该代码以使其正确返回证书后,连接代码工作正常并且SecTrustRef不再为 NULL。

In summary:总之:

1) The Apple documentation, while lacking good examples does appear to be accurate. 1) Apple 文档虽然缺乏很好的例子,但看起来确实是准确的。

2) The reason the SecTrustRef was NULL was because no valid certificate could be found for the connection negotiations phase and that was because no certificates where being made available to the connection API due to the earlier mentioned coding error. 2) SecTrustRef为 NULL 的原因是因为在连接协商阶段找不到有效的证书,这是因为由于前面提到的编码错误,没有证书可用于连接 API。

3) If you are seeing a similar error, my suggestion would be to check and double check your code, because as would be expected, the iOS side of the equation works as documented. 3)如果您看到类似的错误,我的建议是检查并仔细检查您的代码,因为正如预期的那样,等式的 iOS 端按照文档中的说明工作。

I recently created an Obj-C package to handle TLS taking into account the latest restrictions imposed by Apple.考虑到 Apple 施加的最新限制,我最近创建了一个 Obj-C 包来处理 TLS。 Getting the certificates right is a very important step.获得正确的证书是非常重要的一步。

https://github.com/eamonwhiter73/IOSObjCWebSockets https://github.com/eamonwhiter73/IOSObjCWebSockets

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM