iOS:在钥匙串中预先安装SSL证书 – 以编程方式

我想在用户访问该站点之前在钥匙串中安装/保存证书。 我有一个HTTPS服务器,我的应用程序在进入https:// mysite之前对用户进行身份validation。 有没有一种方法可以通过钥匙串中的发布请求来安装/保存证书。 或者我将该证书(文件)复制到资源包,以将其标记为可信。

谢谢

一旦你有der格式的服务器证书,你可以尝试下面的代码:

+ (void) addCertToKeychain:(NSData*)certInDer { OSStatus err = noErr; SecCertificateRef cert; cert = SecCertificateCreateWithData(NULL, (CFDataRef) certInDer); assert(cert != NULL); CFTypeRef result; NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: (id)kSecClassCertificate, kSecClass, cert, kSecValueRef, nil]; err = SecItemAdd((CFDictionaryRef)dict, &result); assert(err == noErr || err == errSecDuplicateItem); CFRelease(cert); } 

它会将证书添加到应用程序的钥匙串沙箱中,即没有其他应用程序会信任您的证书。

来自: http : //blog.asolutions.com/2011/02/using-tls-with-self-signed-certificates-or-custom-root-certificates-in-ios/

您有两个选项可用:将您的服务器的证书添加到钥匙串或手动执行validation。 不pipe你的方法如何,你需要在你的应用程序中包含DER编码的X.509公共证书。 在下面的例子中,它被命名为“ios-trusted-cert.der”),并创build一个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遵循内存所有权的创build规则,所以您必须在不再需要CF时释放内存泄漏。

接下来,您可以将您的证书添加到您的应用程序的钥匙串。 当您希望iOS为您创build的每个新套接字信任您的证书时,这是适当的。

 - (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. } } 

如果您只想validation正在创build的套接字的证书,并且您的应用中没有其他套接字,则可以手动validation您对证书的信任。 首先,创build一个套接字(假设您的服务器正在侦听与您的客户端相同的机器上的端口8443),并在其ssl设置中禁用其证书链validation:

 - (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); } 

然后,当您收到一个callback,您的套接字已准备好写入数据时,您应该在将任何数据写入服务器或从服务器读取任何数据之前validation服务器包含的证书的可信性。 首先(1),使用所连接的服务器的主机名创build一个客户端SSL策略。 主机名包含在服务器的证书中,用于validationDNS所指向的服务器是否是您信任的服务器。 接下来(2),您从套接字中获取实际的服务器证书。 如果服务器的证书是证书链的一部分,则可能有多个与服务器关联的证书。 当你有实际的服务器证书时,你可以(3)创build一个信任对象。 信任对象表示信任评估的本地上下文。 它隔离个人信任评估,而钥匙串证书适用于所有可信套接字。 在您拥有一个信任对象之后,您可以(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; } }