I'm developing a webserver on objetive-c for Iphone and implemented TLS / SSL for encrypted communication with a self sign certificate, presented in the links below.
I have tested the connection using wget
of Ubuntu to create a client and connect to the server.
The problem is when the client connects to the server I would like to force it to use the client certificate ie to make a handshake, at the moment I can not do the same, the client establishes the connection with or without certificate.
#import "WebServerSecurity.h"
#import <sys/socket.h>
#import <netinet/in.h>
@implementation WebServerSecurity
{
CFSocketRef socket;
}
static WebServerSecurity *webServerSecurity;
static CFReadStreamRef readStream = NULL;
static CFWriteStreamRef writeStream = NULL;
static NSInputStream *inputStream=nil;
static NSOutputStream *outputStream=nil;
-(void)startServer
{
webServerSecurity=self;
/************************* CREATE SOCKET ******************************/
CFSocketContext ctx={0,(__bridge void *)(self),NULL,NULL,NULL};
socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, *(CFSocketCallBack)handleConnect, &ctx);
if (!socket)
{
NSLog(@"[Server] Unable to create socket.");
return;
}
/************************* BIND SOCKET ******************************/
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
address.sin_port = htons(8080);
address.sin_addr.s_addr = INADDR_ANY;
CFDataRef addressData = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&address, sizeof(address));
if (CFSocketSetAddress(socket, addressData) != kCFSocketSuccess)
{
NSLog(@"[Server] Unable to bind socket to address.");
return;
}
CFRelease(addressData);
CFRunLoopSourceRef socketSource=CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), socketSource, kCFRunLoopDefaultMode);
}
static void handleConnect (CFSocketRef _socket,CFSocketCallBack type, CFDataRef address,const void *data,void *info)
{
/************************************* CREATE INPUT AND OUTPUT STREAM ********************/
CFSocketNativeHandle sock = *(CFSocketNativeHandle *) data;
CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock,&readStream, &writeStream);
if (!readStream || !writeStream)
{
close(sock);
NSLog(@"CFStreamCreatePairWithSocket() failed\n");
return;
}
/*****************************************************************************************/
/************************************* CONFIGURE CERTIFICATE AND OPEN CONNECTION *****************************/
NSString *certificatePath = [[NSBundle mainBundle]pathForResource:@"server" ofType:@"p12"];
NSData *certicateData = [NSData dataWithContentsOfFile:certificatePath];
CFArrayRef keyRef;
OSStatus status =SecPKCS12Import((__bridge CFDataRef)certicateData,(__bridge CFDictionaryRef)@{(__bridge NSString*)kSecImportExportPassphrase :@"etpick9816"}, &keyRef);
if(status!=noErr)
{
NSLog(@"PKCS12 import error %i",(int)status);
return;
}
CFDictionaryRef identityDict=CFArrayGetValueAtIndex(keyRef, 0);
SecIdentityRef identityRef=(SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
SecCertificateRef certificate;
status = SecIdentityCopyCertificate(identityRef, &certificate);
if (status != noErr)
{
NSLog(@"sec identity copy failed: %i", (int)status);
return;
}
NSArray *certificates = [NSArray arrayWithObjects:(__bridge id)identityRef, (__bridge id)certificate, nil];
inputStream=(__bridge NSInputStream*)readStream;
outputStream=(__bridge NSOutputStream*)writeStream;
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFReadStreamSetProperty(readStream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
CFReadStreamSetProperty(readStream, kCFStreamPropertySSLPeerTrust, kCFStreamPropertySSLPeerTrust);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLPeerTrust, kCFStreamPropertySSLPeerTrust);
NSDictionary *settings = @{(id)kCFStreamSSLCertificates:certificates,(id)kCFStreamSSLIsServer:@YES};
CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (CFDictionaryRef)settings);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (CFDictionaryRef)settings);
[inputStream setDelegate:webServerSecurity];
[outputStream setDelegate:webServerSecurity];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
/*****************************************************************************************/
}
-(void)close
{
NSLog(@"Closing streams.");
CFSocketInvalidate(socket);
}
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
{
switch (streamEvent)
{
case NSStreamEventOpenCompleted:
{
NSLog(@"Stream opened");
break;
}
case NSStreamEventHasSpaceAvailable:
{
if(theStream == outputStream)
{
break;
}
}
case NSStreamEventHasBytesAvailable:
{
if (theStream == inputStream)
{
[self readRequest];
NSString * str = [NSString stringWithFormat:
@"GET / HTTP/1.0\r\n\r\n"];
const uint8_t * rawstring =(const uint8_t *)[str UTF8String];
[outputStream write:rawstring maxLength:str.length];
[outputStream close];
}
break;
}
case NSStreamEventErrorOccurred:
{
NSLog(@"Can not connect to the host!");
break;
}
case NSStreamEventEndEncountered:
{
NSLog(@"EventEndEncountered");
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
theStream=nil;
break;
}
default:
{
NSLog(@"Unknown event");
}
}
}
-(void)readRequest
{
NSLog(@"inputStream is ready.");
uint8_t buf[1024];
unsigned int len = 0;
NSMutableData* data=[[NSMutableData alloc] init];
while ([inputStream hasBytesAvailable])
{
len = [inputStream read:buf maxLength:sizeof(buf)];
if(len > 0)
[data appendBytes: (const void *)buf length:len];
}
[inputStream close];
NSString *dataReceived = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(@"Data received:(%@)",dataReceived);
}
@end