通过 NSStream 与我的证书进行 SSL 握手
SSL Handshake with my certificate by NSStream
我正在为 iOS 编写客户端以通过 SSL/TLS 连接到我的服务器。我决定使用 NSStream 实现。现在我的项目由于 SSL 握手而停止,我不知道如何使用流处理它,也找不到任何委托示例。据我所知,证书传递应该在 NSStreamEventHasSpaceAvailable 中,但实际上我不明白如何。我需要通过我的证书(他没有安装在设备上)。有人可以帮助我吗?
此代码因以下原因而崩溃:
2015-02-09 18:58:28.902 Test CFNetwork SSLHandshake failed (-9807)
2015-02-09 18:58:28.903 Test unexpected NSStreamEventErrorOccurred: Error Domain=NSOSStatusErrorDomain Code=-9807 "The operation couldn’t be completed. (OSStatus error -9807.)"
2015-02-09 18:58:28.903 Test unexpected NSStreamEventErrorOccurred: Error Domain=NSOSStatusErrorDomain Code=-9807 "The operation couldn’t be completed. (OSStatus error -9807.)"
我有:
@interface NSStream (FSNetworkAdditions)
+ (void)qNetworkAdditions_getStreamsToHostNamed:(NSString *)hostName
port:(NSInteger)port
inputStream:(out NSInputStream **)inputStreamPtr
outputStream:(out NSOutputStream **)outputStreamPtr;
@end
@implementation NSStream (FSNetworkAdditions)
+ (void)qNetworkAdditions_getStreamsToHostNamed:(NSString *)hostName
port:(NSInteger)port
inputStream:(out NSInputStream **)inputStreamPtr
outputStream:(out NSOutputStream **)outputStreamPtr
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
assert(hostName != nil);
assert( (port > 0) && (port < 65536) );
assert( (inputStreamPtr != NULL) || (outputStreamPtr != NULL) );
readStream = NULL;
writeStream = NULL;
CFStreamCreatePairWithSocketToHost(
NULL,
(__bridge CFStringRef) hostName,
port,
((inputStreamPtr != NULL) ? &readStream : NULL),
((outputStreamPtr != NULL) ? &writeStream : NULL)
);
if (inputStreamPtr != NULL) {
*inputStreamPtr = CFBridgingRelease(readStream);
}
if (outputStreamPtr != NULL) {
*outputStreamPtr = CFBridgingRelease(writeStream);
}
}
- (void)loadHostName:(NSString *)hostName onPort:(NSInteger)portNumber {
NSString* filePath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"crt"];
NSData *iosTrustedCertDerData = [NSData dataWithContentsOfFile:filePath];
certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) iosTrustedCertDerData);
NSInputStream *inputStream;
NSOutputStream *outputStream;
[NSStream qNetworkAdditions_getStreamsToHostNamed:hostName
port:portNumber
inputStream:&inputStream
outputStream:&outputStream];
[inputStream setProperty:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey];
[outputStream setProperty:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey];
inputStream.delegate = self;
outputStream.delegate = self;
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
这位代表:
- (void)stream:(NSStream *)aStream
handleEvent:(NSStreamEvent)eventCode {
SecPolicyRef policy;
switch (eventCode) {
case NSStreamEventNone:
break;
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
{
int stop = 1;
break;
}
case NSStreamEventHasSpaceAvailable:
{
// #1
// NO for client, YES for server. In this example, we are a client
// replace "localhost" with the name of the server to which you are connecting
policy = SecPolicyCreateSSL(NO, CFSTR("192.168.178.14"));
SecTrustRef trust = NULL;
// #2
CFArrayRef streamCertificates =
(__bridge CFArrayRef)([aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates]);
// #3
SecTrustCreateWithCertificates(streamCertificates,
policy,
&trust);
// #4
SecTrustSetAnchorCertificates(trust,
(__bridge CFArrayRef) [NSArray arrayWithObject:(__bridge id)certificate]); // #5
SecTrustResultType trustResultType = kSecTrustResultInvalid;
OSStatus status = SecTrustEvaluate(trust, &trustResultType);
if (status == errSecSuccess) {
// expect trustResultType == kSecTrustResultUnspecified
// until my cert exists in the keychain see technote for more detail.
if (trustResultType == kSecTrustResultUnspecified) {
NSLog(@"We can trust this certificate! TrustResultType: %d", trustResultType);
} else {
NSLog(@"Cannot trust certificate. TrustResultType: %d", trustResultType);
}
} else {
NSLog(@"Creating trust failed: %d", status);
[aStream close];
}
if (trust) {
CFRelease(trust);
}
if (policy) {
CFRelease(policy);
}
break;
}
case NSStreamEventErrorOccurred:
NSLog(@"unexpected NSStreamEventErrorOccurred: %@", [aStream streamError]);
break;
case NSStreamEventEndEncountered:
break;
default:
break;
}
工作正常:
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:1];
[settings setObject:(NSString *)NSStreamSocketSecurityLevelTLSv1 forKey:(NSString *)kCFStreamSSLLevel];
[settings setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsAnyRoot];
[settings setObject:hostName forKey:(NSString *)kCFStreamSSLPeerName];
[settings setObject:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey];
inputStream.delegate = self;
outputStream.delegate = self;
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
CFReadStreamSetProperty((CFReadStreamRef)inputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty((CFWriteStreamRef)outputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
我正在为 iOS 编写客户端以通过 SSL/TLS 连接到我的服务器。我决定使用 NSStream 实现。现在我的项目由于 SSL 握手而停止,我不知道如何使用流处理它,也找不到任何委托示例。据我所知,证书传递应该在 NSStreamEventHasSpaceAvailable 中,但实际上我不明白如何。我需要通过我的证书(他没有安装在设备上)。有人可以帮助我吗?
此代码因以下原因而崩溃:
2015-02-09 18:58:28.902 Test CFNetwork SSLHandshake failed (-9807)
2015-02-09 18:58:28.903 Test unexpected NSStreamEventErrorOccurred: Error Domain=NSOSStatusErrorDomain Code=-9807 "The operation couldn’t be completed. (OSStatus error -9807.)"
2015-02-09 18:58:28.903 Test unexpected NSStreamEventErrorOccurred: Error Domain=NSOSStatusErrorDomain Code=-9807 "The operation couldn’t be completed. (OSStatus error -9807.)"
我有:
@interface NSStream (FSNetworkAdditions)
+ (void)qNetworkAdditions_getStreamsToHostNamed:(NSString *)hostName
port:(NSInteger)port
inputStream:(out NSInputStream **)inputStreamPtr
outputStream:(out NSOutputStream **)outputStreamPtr;
@end
@implementation NSStream (FSNetworkAdditions)
+ (void)qNetworkAdditions_getStreamsToHostNamed:(NSString *)hostName
port:(NSInteger)port
inputStream:(out NSInputStream **)inputStreamPtr
outputStream:(out NSOutputStream **)outputStreamPtr
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
assert(hostName != nil);
assert( (port > 0) && (port < 65536) );
assert( (inputStreamPtr != NULL) || (outputStreamPtr != NULL) );
readStream = NULL;
writeStream = NULL;
CFStreamCreatePairWithSocketToHost(
NULL,
(__bridge CFStringRef) hostName,
port,
((inputStreamPtr != NULL) ? &readStream : NULL),
((outputStreamPtr != NULL) ? &writeStream : NULL)
);
if (inputStreamPtr != NULL) {
*inputStreamPtr = CFBridgingRelease(readStream);
}
if (outputStreamPtr != NULL) {
*outputStreamPtr = CFBridgingRelease(writeStream);
}
}
- (void)loadHostName:(NSString *)hostName onPort:(NSInteger)portNumber {
NSString* filePath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"crt"];
NSData *iosTrustedCertDerData = [NSData dataWithContentsOfFile:filePath];
certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) iosTrustedCertDerData);
NSInputStream *inputStream;
NSOutputStream *outputStream;
[NSStream qNetworkAdditions_getStreamsToHostNamed:hostName
port:portNumber
inputStream:&inputStream
outputStream:&outputStream];
[inputStream setProperty:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey];
[outputStream setProperty:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey];
inputStream.delegate = self;
outputStream.delegate = self;
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
这位代表:
- (void)stream:(NSStream *)aStream
handleEvent:(NSStreamEvent)eventCode {
SecPolicyRef policy;
switch (eventCode) {
case NSStreamEventNone:
break;
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
{
int stop = 1;
break;
}
case NSStreamEventHasSpaceAvailable:
{
// #1
// NO for client, YES for server. In this example, we are a client
// replace "localhost" with the name of the server to which you are connecting
policy = SecPolicyCreateSSL(NO, CFSTR("192.168.178.14"));
SecTrustRef trust = NULL;
// #2
CFArrayRef streamCertificates =
(__bridge CFArrayRef)([aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates]);
// #3
SecTrustCreateWithCertificates(streamCertificates,
policy,
&trust);
// #4
SecTrustSetAnchorCertificates(trust,
(__bridge CFArrayRef) [NSArray arrayWithObject:(__bridge id)certificate]); // #5
SecTrustResultType trustResultType = kSecTrustResultInvalid;
OSStatus status = SecTrustEvaluate(trust, &trustResultType);
if (status == errSecSuccess) {
// expect trustResultType == kSecTrustResultUnspecified
// until my cert exists in the keychain see technote for more detail.
if (trustResultType == kSecTrustResultUnspecified) {
NSLog(@"We can trust this certificate! TrustResultType: %d", trustResultType);
} else {
NSLog(@"Cannot trust certificate. TrustResultType: %d", trustResultType);
}
} else {
NSLog(@"Creating trust failed: %d", status);
[aStream close];
}
if (trust) {
CFRelease(trust);
}
if (policy) {
CFRelease(policy);
}
break;
}
case NSStreamEventErrorOccurred:
NSLog(@"unexpected NSStreamEventErrorOccurred: %@", [aStream streamError]);
break;
case NSStreamEventEndEncountered:
break;
default:
break;
}
工作正常:
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:1];
[settings setObject:(NSString *)NSStreamSocketSecurityLevelTLSv1 forKey:(NSString *)kCFStreamSSLLevel];
[settings setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsAnyRoot];
[settings setObject:hostName forKey:(NSString *)kCFStreamSSLPeerName];
[settings setObject:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey];
inputStream.delegate = self;
outputStream.delegate = self;
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
CFReadStreamSetProperty((CFReadStreamRef)inputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty((CFWriteStreamRef)outputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);