iOS: предварительная установка сертификата SSL в связке ключей - программно

Я хочу установить / сохранить сертификат в связке ключей до того, как пользователь посетит сайт. У меня есть HTTPS-сервер, и мое приложение аутентифицирует пользователя, прежде чем он перейдет на https://mysite.

Есть ли способ установить / сохранить сертификат с помощью запроса POST в цепочке для ключей или я могу скопировать этот сертификат (файл) в пакет ресурсов, чтобы отметить его как надежный?


person Deam    schedule 16.03.2011    source источник
comment
Вам следует принять ответ или уточнить свои опасения, если они не помогли решить ваши проблемы.   -  person MrTJ    schedule 03.04.2012


Ответы (2)


Получив сертификат сервера в формате 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);
}

Он добавит сертификат в песочницу связки ключей вашего приложения, т.е. никакое другое приложение не будет доверять вашему сертификату.

person MrTJ    schedule 30.03.2012
comment
Спасибо. Однако выполнение этого разрешения не приведет к автоматической аутентификации самозаверяющего сервера, см. мой ответ на другой вопрос. - person xiang; 07.02.2014

От: 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;
  }
}
person Deam    schedule 26.03.2013