Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/104.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ssl/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
iOS:以编程方式在密钥链中预安装SSL证书_Ios_Ssl_Certificate_Installation_Keychain - Fatal编程技术网

iOS:以编程方式在密钥链中预安装SSL证书

iOS:以编程方式在密钥链中预安装SSL证书,ios,ssl,certificate,installation,keychain,Ios,Ssl,Certificate,Installation,Keychain,我想在用户访问站点之前在密钥链中安装/保存证书。 我有一个HTTPS服务器,我的应用程序在用户访问之前对其进行身份验证 是否有一种方法可以通过密钥链中的POST请求安装/保存证书,或者可以将该证书(文件)复制到资源包中以将其标记为可信?一旦您拥有der格式的服务器证书,您可以尝试以下代码: + (void) addCertToKeychain:(NSData*)certInDer { OSStatus err = noErr; SecCertificateR

我想在用户访问站点之前在密钥链中安装/保存证书。 我有一个HTTPS服务器,我的应用程序在用户访问之前对其进行身份验证


是否有一种方法可以通过密钥链中的POST请求安装/保存证书,或者可以将该证书(文件)复制到资源包中以将其标记为可信?

一旦您拥有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);
}
它会将证书添加到您应用程序的密钥链沙箱中,即没有其他应用程序会信任您的证书。

来自:

您有两个可用选项:将服务器的证书添加到密钥链或手动执行验证。无论采用何种方法,您都需要在应用程序中包含DER编码的X.509公共证书。在下面的示例中,它被命名为“ios-trusted-cert.der”),并使用它创建一个SecCertificateRef。(如果服务器的证书是根证书颁发机构链的一部分,则应安装根证书颁发机构,而不是服务器的证书。)

请记住,SecCertificateCreateWithData遵循内存所有权的创建规则,因此您必须在不再需要它时释放它,以避免内存泄漏

接下来,您可以将证书添加到应用程序的钥匙链中。当您希望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;
  }
}

如果他们没有解决您的问题,您应该接受回答或澄清您的担忧。谢谢。不过,这样做不会自动对自签名服务器进行身份验证,请注意另一个问题。
#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;
  }
}