От: http://blog.asolutions.com/2011/02/using-tls-with-self-signed-certificates-or-custom-root-certificates-in-ios/
У вас есть два варианта: добавить сертификат сервера в связку ключей или выполнить проверку вручную. Независимо от вашего подхода, вам нужно будет включить в ваше приложение открытый сертификат X.509 с кодировкой DER. В приведенном ниже примере он называется «ios-trust-cert.der») и создайте с ним SecCertificateRef. (Если сертификат вашего сервера является частью цепочки с корневым центром сертификации, вам следует установить корневой центр сертификации, а не сертификат вашего сервера.)
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSData *iosTrustedCertDerData =
[NSData dataWithContentsOfFile:[bundle pathForResource:@"ios-trusted-cert"
ofType:@"der"]];
SecCertificateRef certificate =
SecCertificateCreateWithData(NULL,
(CFDataRef) iosTrustedCertDerData);
Помните, что SecCertificateCreateWithData следует правилу создания владения памятью, поэтому вы должны CFRelease, когда он вам больше не нужен, чтобы избежать утечек памяти.
Далее вы можете добавить свой сертификат в связку ключей вашего приложения. Это уместно, когда вы хотите, чтобы iOS доверял вашему сертификату для каждого нового сокета, который вы создаете.
- (void) useKeychain: (SecCertificateRef) certificate {
OSStatus err =
SecItemAdd((CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(id) kSecClassCertificate, kSecClass,
certificate, kSecValueRef,
nil],
NULL);
if ((err == noErr) || // success!
(err == errSecDuplicateItem)) { // the cert was already added. Success!
// create your socket normally.
// This is oversimplified. Refer to the CFNetwork Guide for more details.
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@"localhost",
8443,
&readStream,
&writeStream);
CFReadStreamSetProperty(readStream,
kCFStreamPropertySocketSecurityLevel,
kCFStreamSocketSecurityLevelTLSv1);
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
} else {
// handle the error. There is probably something wrong with your cert.
}
}
Если вы хотите проверить сертификат только для сокета, который вы создаете, и для других сокетов в вашем приложении нет, вы можете проверить свое доверие к сертификату вручную. Сначала создайте сокет (при условии, что ваш сервер прослушивает порт 8443 на том же компьютере, что и ваш клиент) и отключите проверку цепочки сертификатов в настройках ssl:
- (void) verifiesManually: (SecCertificateRef) certificate {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@"localhost",
8443,
&readStream,
&writeStream);
// Set this kCFStreamPropertySocketSecurityLevel before
// setting kCFStreamPropertySSLSettings.
// Setting kCFStreamPropertySocketSecurityLevel
// appears to override previous settings in kCFStreamPropertySSLSettings
CFReadStreamSetProperty(readStream,
kCFStreamPropertySocketSecurityLevel,
kCFStreamSocketSecurityLevelTLSv1);
// this disables certificate chain validation in ssl settings.
NSDictionary *sslSettings =
[NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain,
nil];
CFReadStreamSetProperty(readStream,
kCFStreamPropertySSLSettings,
sslSettings);
NSInputStream *inputStream = (NSInputStream *)readStream;
NSOutputStream *outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
}
Затем, когда вы получите обратный вызов о том, что ваш сокет готов к записи данных, вы должны проверить доверие к сертификату, который включил ваш сервер, прежде чем записывать какие-либо данные на сервер или читать их с сервера. Сначала (1) создайте клиентскую политику SSL с именем хоста сервера, к которому вы подключились. Имя хоста включено в сертификат сервера для проверки подлинности того, что сервер, на который направил вас DNS, является сервером, которому вы доверяете. Далее (2) вы извлекаете действительные сертификаты сервера из сокета. С сервером может быть связано несколько сертификатов, если сертификат сервера является частью цепочки сертификатов. Когда у вас есть действительные сертификаты сервера, вы можете (3) создать объект доверия. Объект доверия представляет локальный контекст для оценки доверия. Он изолирует отдельные оценки доверия, тогда как сертификаты цепочки ключей применяются ко всем доверенным сокетам. После того, как у вас есть объект доверия, вы можете (4) установить сертификаты привязки, которые являются сертификатами, которым вы доверяете. Наконец (5) вы можете оценить объект доверия и выяснить, можно ли доверять серверу.
#pragma mark -
#pragma mark NSStreamDelegate
- (void)stream:(NSStream *)aStream
handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventNone:
break;
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
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
SecPolicyRef policy = SecPolicyCreateSSL(NO, CFSTR("localhost"));
SecTrustRef trust = NULL;
// #2
CFArrayRef streamCertificates =
[aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates];
// #3
SecTrustCreateWithCertificates(streamCertificates,
policy,
&trust);
// #4
SecTrustSetAnchorCertificates(trust,
(CFArrayRef) [NSArray arrayWithObject:(id) self.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;
}
}