问题
I'm using CFStream/NSStream to make http connections. I want to be able to detect that a SSL handshake fails for three cases:
- case A: server is not trusted
- case B: server is trusted but asks a client certificate
- case C: server is not trusted and it asks a client certificate
Today without doing anithing on the SSL Properties of my CFStream, I get:
- case A: error -9807
- case B: no error but server refuses the connection (error 500)
- case C: error 9807
Is there a way to configure CFStream to correctly distinguish these 3 cases ? Or to have some callbacks during SSL handshake ?
Thanks for your help.
回答1:
Some time ago I ran in a same thing with CFSockets using SSL. CFStream handles all the handshake stuff. I wrote a little class addition for NSStream (Base code comes from Apple, don't have the link anymore, if I find it I'll add it). That's what worked for me.
Interface
@interface NSStream (FSNetworkAdditions)
+ (void)qNetworkAdditions_getStreamsToHostNamed:(NSString *)hostName
port:(NSInteger)port
inputStream:(out NSInputStream **)inputStreamPtr
outputStream:(out NSOutputStream **)outputStreamPtr;
@end
And Implementation
#import "FSNetworkAdditions.h"
@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,
(CFStringRef) hostName,
port,
((inputStreamPtr != NULL) ? &readStream : NULL),
((outputStreamPtr != NULL) ? &writeStream : NULL)
);
NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots,
[NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
//kCFNull,kCFStreamSSLPeerName,
kCFStreamSocketSecurityLevelSSLv3, kCFStreamSSLLevel,
[NSNumber numberWithBool:YES], kCFStreamPropertyShouldCloseNativeSocket,
nil];
if (readStream) {
CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
}
if (writeStream) {
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
}
if (inputStreamPtr != NULL) {
*inputStreamPtr = CFBridgingRelease(readStream);
}
if (outputStreamPtr != NULL) {
*outputStreamPtr = CFBridgingRelease(writeStream);
}
}
@end
That's it now you can connect to a Server like this:
NSInputStream *inputStream;
NSOutputStream *outputStream;
[NSStream qNetworkAdditions_getStreamsToHostNamed:host
port:port
inputStream:&inputStream
outputStream:&outputStream];
inputStream.delegate = self;
outputStream.delegate = self;
Where "self" conforms to the NSStreamDelegate Protocol.
I hope these snippets helps.
来源:https://stackoverflow.com/questions/13534091/how-to-configure-cfstream-or-nsstream-for-ssl-handshake