Frage iOS: Pre-Installation SSL-Zertifikat im Schlüsselbund - programmgesteuert


Ich möchte ein Zertifikat im Schlüsselbund installieren / speichern, bevor der Benutzer die Website besucht. Ich habe einen HTTPS-Server, und meine App authentifiziert den Benutzer, bevor er auf die https: // Meine Seite. Gibt es eine Möglichkeit, das Zertifikat per Post-Anfrage im Schlüsselbund zu installieren / speichern. ODER Ich kopiere dieses Zertifikat (die Datei) in das Ressourcenpaket, um es als vertrauenswürdig zu markieren.

Vielen Dank

al


16
2018-03-16 10:04


Ursprung


Antworten:


Sobald Sie das Server-Zertifikat im Format haben, können Sie den folgenden Code ausprobieren:

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

Es wird das Zertifikat der Schlüsselbund-Sandbox Ihrer Anwendung hinzufügen, d. H. Keine andere Anwendung wird Ihrem Zertifikat vertrauen.


14
2018-03-30 10:51



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

Sie haben zwei Möglichkeiten: Fügen Sie das Zertifikat Ihres Servers zum Schlüsselbund hinzu oder führen Sie eine manuelle Validierung durch. Unabhängig von Ihrem Ansatz müssen Sie ein DER-codiertes öffentliches X.509-Zertifikat in Ihre App aufnehmen. Im folgenden Beispiel heißt es "ios-trusted-cert.der" und erstellt damit ein SecCertificateRef. (Wenn das Zertifikat Ihres Servers Teil einer Kette zu einer Stammzertifizierungsstelle ist, sollten Sie die Stammzertifizierungsstelle anstelle des Zertifikats Ihres Servers installieren.)

NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSData *iosTrustedCertDerData =
  [NSData dataWithContentsOfFile:[bundle pathForResource:@"ios-trusted-cert"
                                                    ofType:@"der"]];
SecCertificateRef certificate =
  SecCertificateCreateWithData(NULL,
                               (CFDataRef) iosTrustedCertDerData);

Denken Sie daran, dass SecCertificateCreateWithData der Erstellungsregel des Speichereigentums folgt. Daher müssen Sie CFFree freigeben, wenn Sie es nicht mehr benötigen, um Speicherlecks zu vermeiden.

Als Nächstes können Sie Ihr Zertifikat zum Schlüsselbund Ihrer App hinzufügen. Dies ist sinnvoll, wenn iOS Ihrem Zertifikat für jeden neuen von Ihnen erstellten Socket vertrauen soll.

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

Wenn Sie nur das Zertifikat für den von Ihnen erstellten Socket und für keine anderen Sockets in Ihrer App überprüfen möchten, können Sie das Vertrauen in das Zertifikat manuell überprüfen. Erstellen Sie zuerst einen Socket (vorausgesetzt, Ihr Server überwacht Port 8443 auf demselben Computer wie Ihr Client) und deaktivieren Sie die Gültigkeitsprüfung der Zertifikatkette in den folgenden SSL-Einstellungen:

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

Wenn Sie dann eine Rückmeldung erhalten, dass Ihr Socket zum Schreiben von Daten bereit ist, sollten Sie die Vertrauenswürdigkeit des Zertifikats überprüfen, das Ihr Server enthielt, bevor Sie Daten in den Server schreiben oder Daten daraus lesen. Erstellen Sie zuerst (1) eine Client-SSL-Richtlinie mit dem Hostnamen des Servers, mit dem Sie verbunden sind. Der Hostname ist im Zertifikat des Servers enthalten, um zu bestätigen, dass der Server, an den Sie von DNS verwiesen wird, der Server ist, dem Sie vertrauen. Als nächstes (2) greifen Sie die tatsächlichen Serverzertifikate aus dem Socket. Dem Server können mehrere Zertifikate zugeordnet sein, wenn das Zertifikat des Servers Teil einer Zertifikatkette ist. Wenn Sie über die tatsächlichen Serverzertifikate verfügen, können Sie (3) ein Vertrauensobjekt erstellen. Das Vertrauensobjekt stellt einen lokalen Kontext für Vertrauensbewertungen dar. Es isoliert einzelne Vertrauensbewertungen, während die Schlüsselbundzertifikate für alle vertrauenswürdigen Sockets gelten. Nachdem Sie ein Vertrauensobjekt haben, können Sie (4) die Ankerzertifikate festlegen, die die Zertifikate sind, denen Sie vertrauen. Abschließend (5) können Sie das Vertrauensobjekt auswerten und feststellen, ob der Server vertrauenswürdig ist.

#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;
  }
}

7
2018-03-26 11:06