jus*_*sij 17 c# ssl objective-c tcp-ip ios
我正在尝试使用TCP/IP上的TLS将iOS应用程序连接到Windows C#服务器.
所述TLS连接使用不受信任的证书由一个创建不可信CA根证书使用makecert实用程序.
为了测试这些证书,我创建了一个简单的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;
}
Run Code Online (Sandbox Code Playgroud)
代码似乎也进行某种形式的TLS协商,因为如果未在NSStream设置中提供p12证书,C#服务器会拒绝连接.
所以看起来TLS谈判的第一阶段正在发挥作用.
为了验证服务器证书,我有这个函数,它由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;
}
Run Code Online (Sandbox Code Playgroud)
现在的问题是,无论我尝试什么,trustRef对象始终为null.
从这个Apple开发人员链接: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
Run Code Online (Sandbox Code Playgroud)
但那得出了相同的结果:(
我只是恢复到上面显示的版本,因为根据我的Google搜索,这似乎是一个相当代码常见的模式.
例如,即使Apple Developer网站的代码也使用了非常相似的样式:
正如我之前提到的,我不是Objective-C的专家(远非如此),所以我可能错了,但从我所看到的情况来看,将这些项目移到课堂中让它们持续存在似乎没有任何区别.
由于似乎对这个问题有些兴趣,我决定用答案更新问题,并详细说明如何最终解决这个问题。
首先是一些背景知识。我从以前的开发人员那里继承了这段代码,我的角色是让损坏的代码正常工作。
我花了很多时间使用 Apple iOS 开发人员网页上的详细信息编写和重新编写连接代码,但似乎没有任何效果。
我最终决定仔细看看这个函数,我继承并错误地假设的代码正在工作:
[self loadClientCertificates:certificates];
Run Code Online (Sandbox Code Playgroud)
乍一看,代码看起来不错。该函数只是从文件加载证书。但经过仔细检查,虽然代码正确加载了证书,但它并没有将这些证书返回给调用者!
修复该代码以正确返回证书后,连接代码工作正常并且 SecTrustRef不再为 NULL。
总之:
1) Apple 文档虽然缺乏好的示例,但似乎确实是准确的。
2) SecTrustRef为 NULL 的原因是因为在连接协商阶段找不到有效的证书,并且由于前面提到的编码错误,没有可用于连接 API 的证书。
3)如果您看到类似的错误,我的建议是检查并仔细检查您的代码,因为正如预期的那样,等式的 iOS 端按照记录工作。
| 归档时间: |
|
| 查看次数: |
713 次 |
| 最近记录: |