如何实现客户端证书和服务器身份验证的iOS

问题描述:

我最近经历了极其艰苦的过程去建立的东西,应该是很简单,但又似乎是在任何一个地方基本未容易找到。我想在这里尝试把一切都问,如果我做错什么,如果没有,来帮助任何人谁需要这些信息。如何实现客户端证书和服务器身份验证的iOS

背景:产品/服务,我试图提供担保是围绕Windows服务器可接入WCF服务内置虽然只有一台PC或一台iPad的自定义客户端应用程序。每个客户一台服务器,无需浏览器访问。一切都已经通过使用Windows标准机制和商业CA认证的认证和授权进行TLS保护。

为了进一步限制访问,使用自签名证书为Windows平台实施了客户端/服务器证书(在没有公共/浏览器访问的相互身份验证的情况下不需要商业CA--尽管有相反的说法 - 他们很难管理)。

获得这一切,为iPad的工作是一个可怕的噩梦记录用虚假或部分正确建议的数量惊人。在下文中,我试图链接到的最佳来源,但我很抱歉,如果我无意中错过了归属。请评论这篇文章是否有任何错误/误导。

感谢

的主要步骤为:

  1. 创建生成证书的系统(容易的,但不平凡的,如果它是一个生产系统)
  2. 转让证书iPad的(没有嵌入)
  3. 将所有收到的凭证保存在应用程序钥匙串中(其中苹果说它们属于)
  4. 检索已保存的凭证f ROM在NSURLConnections钥匙扣使用
  5. 其实验证服务器证书,并返回客户端凭证

步骤1.生成证书

编号:http://developer-should-know.tumblr.com/post/127063737582/how-to-create-your-own-pki-with-openssl

您可以使用其他方法,但是OpenSSL,以便Windows的[http://slproweb.com/products.html]是相当真棒,除了标准的接口是CMDLINE和文档是难走。

我希望有人什么都得我前面的解释是显而易见的,但不是: [A]的应用程序安装到根目录的目录,而且涵盖了默认用于未在命令中指定的设置配置文件线 并[b]的中间和输出文件的位置应该在配置文件中 并[c]某些文件需要由手工创建运行的命令 [d]你应该建立适合文件/文件夹结构之前指定你正在尝试做什么,然后相应地自定义cfg文件。

在我而言,这意味着一个根CA为我公司,客户的每一个中间证书(设置为好,只会使客户证书),每个客户的服务器证书,客户端证书的要求。(这是最低配置,从不使用CA /客户端对,养根在密码箱) 这是我的文件结构:

c:\sslcert 
    root 
    certs 
     YourCompany (duplicate this structure as required) 
      intermediate 
      server 
      client 
      crl (optional) 

在顶层的sslcert文件夹

.rnd  (empty file) 
certindex.txt (empty file) 
serial.txt (Text file seeded with the text “01”, hold the quotes) 

在根文件夹

RootCA.cfg 

在证书\模板文件夹

IntermediateCA.cfg 

设置工作目录,并推出OpenSSL的 CD \的sslcert C:\的OpenSSL的Win32 \ BIN \ openssl.exe

一步到位

req -config ./root/RootCA.cfg -new -x509 -days 7300 -extensions v3_ca -keyout root/YourCompanyRootCAkey.pem -out root/YourCompanyRootCAcert.cer 

注意对于初学者创建根密钥和证书: -extensions允许您选择在同一个cfg文件中应用多个小节中的一个小节。

检查密钥和证书(可选)

x509 -noout -text -in root/YourCompanyRootCAcert.cer 

请求使用根证书的新中间证书

req -config certs/YourCompany/IntermediateCA.cfg -new -keyout certs/YourCompany/intermediate/intermediateCAkey.pem -out certs/YourCompany/intermediate/intermediateCAreq.pem   

注册中间证书在根配置

ca -config root/RootCA.cfg -extensions v3_intermediate_ca -days 3650 -notext -in certs/YourCompany/intermediate/intermediateCAreq.pem -out certs/YourCompany/intermediate/YourCompanyIntermediateCAcert.cer 

检查发现密钥和证书(可选)

x509 -noout -text -in certs/YourCompany/intermediate/YourCompanyIntermediateCAcert.cer 

通过连接中间和根证书创建证书链文件(这是通过命令行只是一个简单的附加 - 新的链将被加入到最终的P12包)

c:\sslcert> type c:\sslcert\certs\YourCompany\intermediate\YourCompanyIntermediateCAcert.cer c:\sslcert\root\YourCompanyRootCAcert.cer > c:\sslcert\certs\YourCompany\intermediate\YourCompanyCAchain.cer 

请求与中间权威新的客户端密钥和证书

genrsa -aes256 -out certs/YourCompany/client/YourCompanyClientkey.pem 2048 
req -config certs/YourCompany/IntermediateCA.cfg -key 
certs/YourCompany/client/YourCompanyClientkey.pem -new -sha256 -out   certs/YourCompany/client/YourCompanyClientreq.pem 

注册和测试客户端证书

ca -config certs/YourCompany/IntermediateCA.cfg -extensions usr_cert -days 1095 -notext -md sha256 -in certs/YourCompany/client/YourCompanyClientreq.pem -out certs/YourCompany/client/YourCompanyClientcert.cer 
x509 -noout -text -in certs/YourCompany/client/YourCompanyClientcert.cer 
verify -CAfile certs/YourCompany/intermediate/YourCompanyCAchain.cer certs/YourCompany/client/YourCompanyClientcert.cer 

包的客户端证书

pkcs12 -export -in certs/YourCompany/client/YourCompanyClientcert.cer -name “YourCompany Smips Client” -inkey certs/YourCompany/client/YourCompanyClientkey.pem -certfile certs/YourCompany/intermediate/YourCompanyCAchain.cer -out certs/YourCompany/client/YourCompanyClientWithName.p12 

重命名导入到iOS设备的电子邮件PKCS/iTunes的

c:\sslcert> copy c:\sslcert\certs\YourCompany\client\YourCompanyClient.p12 c:\sslcert\certs\YourCompany\client\YourCompanyClient.yourext12 

申请一个新的服务器密钥和证书

genrsa -aes256 -out certs/YourCompany/server/YourCompanyServerkey.pem 2048 
req -config certs/YourCompany/IntermediateCA.cfg -key certs/YourCompany/server/YourCompanyServerkey.pem -new -sha256 -out certs/YourCompany/server/YourCompanyServerreq.pem 

注册和测试与中间权威

ca -config certs/YourCompany/IntermediateCA.cfg -extensions server_cert -days 1095 -notext -md sha256 -in certs/YourCompany/server/YourCompanyServerreq.pem -out certs/YourCompany/server/YourCompanyServercert.cer 
x509 -noout -text -in certs/YourCompany/server/YourCompanyServercert.cer 
verify -CAfile certs/YourCompany/intermediate/YourCompanyCAchain.cer certs/YourCompany/server/YourCompanyServercert.cer 

包服务器证书

pkcs12 -export -in certs/YourCompany/server/YourCompanyServercert.cer -name “YourCompany Smips Server” -inkey certs/YourCompany/server/YourCompanyServerkey.pem -certfile certs/YourCompany/intermediate/YourCompanyCAchain.cer -out certs/YourCompany/server/YourCompanyServer.p12 

这里有CFG文件服务器证书: 根

dir     = . 

[ ca ] 
default_ca    = CA_default 

[ CA_default ] 
serial    = $dir/serial.txt 
database    = $dir/certindex.txt 
new_certs_dir   = $dir/certs 
certs     = $dir/certs 
private_key    = $dir/root/yourcompanyRootCAkey.pem 
certificate    = $dir/root/yourcompanyRootCAcert.cer 
default_days   = 7300 
default_md    = sha256 
preserve    = no 
email_in_dn    = no 
nameopt    = default_ca 
certopt    = default_ca 
policy    = policy_strict 

[ policy_strict ] 
countryName     = match 
stateOrProvinceName   = match 
organizationName   = match 
organizationalUnitName  = optional 
commonName     = supplied 
emailAddress    = optional 

[ req ] 
default_bits   = 4096  # Size of keys 
default_keyfile   = key.pem  # name of generated keys 
default_md    = sha256  # message digest algorithm 
string_mask    = nombstr  # permitted characters 
distinguished_name  = req_distinguished_name 
x509_extensions   = v3_ca 

[ req_distinguished_name ] 
0.organizationName   = Organization Name 
organizationalUnitName  = Organizational Unit Name 
emailAddress     = Email Address 
emailAddress_max   = 40 
localityName   = Locality Name (city, district) 
stateOrProvinceName  = State or Province Name (full name) 
countryName    = Country Name (2 letter code) 
countryName_min   = 2 
countryName_max   = 2 
commonName    = Common Name (hostname, IP, or your name) 
commonName_max   = 64 

0.organizationName_default = yourcompany 
organizationalUnitName_default = yourcompanyRoot Certification 
emailAddress_default  = [email protected] 
localityName_default  = Okeefenokee 
stateOrProvinceName_default = Wisconsin 
countryName_default  = US 

[ v3_ca ] 
subjectKeyIdentifier = hash 
authorityKeyIdentifier = keyid:always,issuer 
basicConstraints = critical, CA:true 
keyUsage = critical, digitalSignature, cRLSign, keyCertSign 

[ v3_intermediate_ca ] 
subjectKeyIdentifier = hash 
authorityKeyIdentifier = keyid:always,issuer 
basicConstraints = critical, CA:true, pathlen:0 
keyUsage = critical, digitalSignature, cRLSign, keyCertSign 

[ crl_ext ] 
authorityKeyIdentifier=keyid:always 

中级

dir = . 

# [For non-command-line folks, everything is keyed to the working directory here (.) so if your working prompt says c:\sslcerts> then the cfg will look for serial.txt at c:\sslcerts\serial.txt and bomb if it doesn’t find things laid out accordingly. Thats why you set up a directory structure to match these entries] 

[ ca ] 
default_ca    = CA_default 

[ CA_default ] 
serial     = $dir/serial.txt 
database    = $dir/certindex.txt 
crl_dir     = $dir/certs/yourcompany/crl 
new_certs_dir    = $dir/certs 
certs     = $dir/certs 
private_key    = $dir/certs/yourcompany/intermediate/IntermediateCAkey.pem 
certificate    = $dir/certs/yourcompany/intermediate/yourcompanyIntermediateCAcert.cer 
default_days    = 3650 
default_md    = sha256 
preserve    = no 
email_in_dn    = no 
nameopt     = default_ca 
certopt     = default_ca 
crlnumber    = $dir/certs/yourcompany/crl/crlnumber 
crl     = $dir/certs/yourcompany/crl/crl.pem 
crl_extensions    = crl_ext 
default_crl_days   = 365 
policy     = policy_loose 

[ policy_loose ] 
countryName      = optional 
stateOrProvinceName    = optional 
localityName     = optional 
organizationName    = optional 
organizationalUnitName   = optional 
commonName      = supplied 
emailAddress     = optional 

[ req ] 
default_bits    = 4096    # Size of keys 
default_keyfile    = $dir/certs/yourcompany/intermediate/IntermediateCAkey.pem 
default_md    = sha256   # message digest 

# the old default was md1 - change this] 

algorithm 
string_mask    = nombstr   # permitted characters 
distinguished_name   = req_distinguished_name 
x509_extensions    = v3_intermediate_ca 

[ req_distinguished_name ] 
0.organizationName     = Organization Name 
organizationalUnitName    = Organizational Unit Name 
emailAddress      = Email Address 
emailAddress_max   = 40 
localityName    = Locality Name (city, district) 
stateOrProvinceName   = State or Province Name (full name) 
countryName    = Country Name (2 letter code) 
countryName_min    = 2 
countryName_max    = 2 
commonName    = Common Name (hostname, IP, or your name) 
commonName_max    = 64 

0.organizationName_default  = yourcompany 
organizationalUnitName_default  = yourcompany Intermediate Certification 
emailAddress_default   = [email protected] 
localityName_default   = Okeefenokee 
stateOrProvinceName_default  = Wisconsin [should be spelled out] 
countryName_default   = US 

[ v3_intermediate_ca ] 
subjectKeyIdentifier   = hash 
authorityKeyIdentifier   = keyid:always,issuer 
basicConstraints   = critical, CA:true, pathlen:0 
keyUsage    = critical, digitalSignature, cRLSign, keyCertSign 

# Important - the pathlen parameter prevents this cert from being used to create new intermediate certs. The subsequent subsections for server and client certs allows you to specify their type and intended usage, as distinct from the intermediate cert, in the same cfg file 

[ usr_cert ] 
basicConstraints   = CA:FALSE 
nsCertType    = client, email 
nsComment    = "OpenSSL Generated Client Certificate" 
subjectKeyIdentifier   = hash 
authorityKeyIdentifier   = keyid,issuer 
keyUsage    = critical, nonRepudiation, digitalSignature, keyEncipherment 
extendedKeyUsage   = clientAuth, emailProtection 

[ server_cert ] 
basicConstraints   = CA:FALSE 
nsCertType    = server 
nsComment    = "OpenSSL Generated Server Certificate" 
subjectKeyIdentifier   = hash 
authorityKeyIdentifier   = keyid,issuer:always 
keyUsage    = critical, digitalSignature, keyEncipherment 
extendedKeyUsage   = serverAuth 

[ crl_ext ] 
authorityKeyIdentifier   = keyid:always 

2.将证书iPad的

编号:how to register the app to open the pdf file in my app in ipad

Apple建议由注册您的应用程序来处理新的文件类型,并与新的自定义扩展名重命名为P12文件手动传输到设备(或电子邮件)安装客户端证书。 p12文件应包含公共证书链以及上述步骤1中定义的客户端证书信息。当您尝试打开这样的文件时,设备会向您的应用程序委托发送一个启动/唤醒,您需要处理该文件(不在didload中,因为它可能是唤醒)。

这已经改变了一点与v8或9,但我需要支持7,所以这是不赞成的处理程序。尽管同样的解决方案,它开始添加到应用程序plist文件,如下面的截图所示。

请注意,您将需要两个新图标和文件扩展名是不太可能被其他应用程序

enter image description here

enter image description here

接下来你需要委托/处理器应该是自我申索-explanatory。由于这部分与正常控制流无关,因此我正在处理AppDelegate.m中的所有委托处理。 (这是错的?)的要求,并请忽略文件的存在偏执额外的检查设置的方法/变量...

编号:https://www.raywenderlich.com/6475/basic-security-in-ios-5-tutorial-part-1

- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication 
      annotation:(id)annotation { 

    if (url) { 

     self.p12Data = [NSData dataWithContentsOfFile:[url path]]; 

     if (!p12Data) { 
      [self messageBox:@"Warning" : @"Failed to read data file, cancelling certificate import"]; 
     } 
     else { 
      [self presentAlertViewForPassPhrase]; 
     } 

     NSFileManager * fileManager = [NSFileManager defaultManager]; 
     if ([fileManager fileExistsAtPath:[url path]]) { 
      [fileManager removeItemAtPath:[url path] error:NULL]; 
     } 
    } 

    return YES; 
} 

- (void)presentAlertViewForPassPhrase { 

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Certificate Credentials" 
                message:@"Please enter the passphrase for your certificate" 
                delegate:self 
              cancelButtonTitle:@"Cancel" 
              otherButtonTitles:@"Done", nil]; 
    [alert setAlertViewStyle:UIAlertViewStyleSecureTextInput]; 
    [alert show]; 
} 

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { 

    if (buttonIndex == 1) {            // User selected "Done" 
     UITextField *ppField = [alertView textFieldAtIndex:0]; 
     if ([ppField.text length] > 0) { 
      [self loadCertificates:ppField.text]; 
     } 
     //Handle Else 
    } 
    else 
    {                 // User selected "Cancel" 
     [self messageBox:@"Information" : @"Certificate import cancelled"]; 
    } 
} 

3.保存在所接收的凭证应用程序钥匙串

既然您已经有了原始的p12数据,那么应该很容易找出下一步该做什么...不。所有文档似乎都是用于名称/密码存储的,可怕的海报数量表明将服务器证书保存到文件系统中,这是可以的,但是当你拥有钥匙串时,它绝对没有意义,苹果公司表示这就是它的目的。最后但并非最不重要的是,您如何区分已存储的证书以及如何更新它们?

长话短说,我决定在尝试各种不起作用的事情来检查它是否应该是更新或初始负载之后,做出完整的删除/重新保存 - 除此之外,我想在第一个因为它是我的应用程序链。所有这些都是CF的东西,我没有使用ARC,因为我拒绝移植任何我不需要的东西。就我所知,只要你分配CF,投给NS,并且CFRelease在使用后没有警告。

这些键引用:

Enumerate all Keychain items in my iOS application

[必要的帮助显示您的钥匙串是什么样子]

How to delete all keychain items accessible to an app?

What makes a keychain item unique (in iOS)?

http://help.sap.com/saphelp_smp307sdk/helpdata/en/7c/03830b70061014a937d8267bb3f358/content.htm

[https://developer.apple.com/library/ios/samplecode/AdvancedURLConnections/Listings/Credentials_m.html,它说:

// IMPORTANT: SecCertificateRef's are not uniqued (that is, you can get two 
// different SecCertificateRef values that described the same fundamental 
// certificate in the keychain), nor can they be compared with CFEqual. So 
// we match up certificates based on their data values. 

的总结是,(杜)最容易做的事情是一个标签分配给证书,所以你可以看看它独特的实现,如果你保存身份将被自动分解为密钥和证书,这可能 - 不确定 - 导致替换时出现一些困难。

代码(说明如下):

- (void) loadCertificates:(NSString *)passPhrase { 

    BOOL lastError = false; 
    NSMutableDictionary * p12Options = [[NSMutableDictionary alloc] init]; 
    [p12Options setObject:passPhrase forKey:(id)kSecImportExportPassphrase]; 
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); 
    OSStatus err = SecPKCS12Import((CFDataRef)p12Data, (CFDictionaryRef)p12Options, &items); 
    if (err != noErr) { 
     [self messageBox:@"Error" : @"Unable to extract security information with the supplied credentials. Please retry"]; 
     lastError = true; 
    } 
    if (!lastError && err == noErr && CFArrayGetCount(items) > 0) { 
     CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0); 
     //Clean-up 

     NSArray *secItemClasses = [NSArray arrayWithObjects: 
            (id)kSecClassCertificate, 
            (id)kSecClassKey, 
            (id)kSecClassIdentity, 
            nil]; 

     for (id secItemClass in secItemClasses) { 
      NSDictionary *spec = @{(id)kSecClass: secItemClass}; 
      err = SecItemDelete((CFDictionaryRef)spec); 
     } 

     //Client Identity & Certificate 

     SecIdentityRef clientIdentity = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity); 

     NSDictionary *addIdentityQuery = [NSDictionary dictionaryWithObjectsAndKeys: 
              kClientIdentityLabel, kSecAttrLabel, 
              (id)clientIdentity, kSecValueRef, 
              nil]; 
     err = SecItemAdd((CFDictionaryRef)addIdentityQuery, NULL); 
     if (err == errSecDuplicateItem) { 
      NSLog(@"Duplicate identity"); 
     } 
     if (err != noErr) { 
      [self messageBox:@"Warning" : @"Failed to save the new identity"]; 
      lastError = true; 
     } 
     //Server Certificate 
     CFArrayRef chain = CFDictionaryGetValue(identityDict, kSecImportItemCertChain); 
     CFIndex N = CFArrayGetCount(chain); 
     BOOL brk = false; 
     for (CFIndex i=0; (i < N) && (brk == false); i++) { 
      SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(chain, i); 
      CFStringRef summary = SecCertificateCopySubjectSummary(cert); 
      NSString* strSummary = [[NSString alloc] initWithString:(NSString *)summary]; 
      if ([strSummary containsString:@"Root"] || (i == N)) { 

       NSDictionary *addCertQuery = [NSDictionary dictionaryWithObjectsAndKeys: 
               kServerCertificateLabel, kSecAttrLabel, 
               (id)cert, kSecValueRef, 
               nil]; 
       err = SecItemAdd((CFDictionaryRef)addCertQuery, NULL); 
       if (err == errSecDuplicateItem) { 
        NSLog(@"Duplicate root certificate"); 
      } 
      if (err != noErr) { 
       [self messageBox:@"Warning" : @"Failed to save the new server certificate"]; 
       lastError = true; 
      } 
      brk = true; 
     } 
     [strSummary release]; 
     CFRelease(summary); 
    } 
} 
else { 
    [self messageBox:@"Error" : @"Unable to extract security information with the supplied credentials. Please retry"]; 
    lastError = true; 
} 
    [p12Options release]; 
    CFRelease(items); 
    if (!lastError) [self messageBox:@"Information" : @"Certificate import succeeded"]; 
} 

其中kClientIdentityLabel和kServerCertificateLabel是任意的标签。

kSec函数太多/复杂,无法在此详细解释。只需说明一切都已清除,然后保存提取的客户端身份,然后提取根CA,然后单独保存。为什么是循环?因为我不知道假设根在链的末端是否在技术上是正确的,但是如果我生成p12,那么现在代码就在那里。从错误昆船编码所以这个网站

注是不可缺少的:https://www.osstatus.com

4.从钥匙扣检索保存的凭据

一旦凭据在钥匙串,你可以提取正是如此他们(故障模式留下遗憾的地方):

- (void) reloadCredentials { 

    self.clientCredential = nil; 
    self.serverCertificateData = nil; 

    if (self.useClientCertificateIfPresent) { 

     NSDictionary* idQuery = [NSDictionary dictionaryWithObjectsAndKeys: 
           kClientIdentityLabel,   kSecAttrLabel, 
           (id)kSecClassIdentity,   kSecClass, 
           kCFBooleanTrue,     kSecReturnRef, 
           kSecMatchLimitAll,    kSecMatchLimit, 
           nil]; 
     CFArrayRef result = nil; 
     OSStatus err = SecItemCopyMatching((CFDictionaryRef)idQuery, (CFTypeRef*)&result); 
     if (err == errSecItemNotFound) { 
      [self messageBox:@"Warning" : @"Client credentials not found. Server connection may fail"]; 
     } 
     else if (err == noErr && result != nil) { 

      SecIdentityRef clientIdentity = (SecIdentityRef)CFArrayGetValueAtIndex(result, 0); 

      SecCertificateRef clientCertificate; 
      SecIdentityCopyCertificate(clientIdentity, &clientCertificate); 
      const void *certs[] = { clientCertificate }; 
      CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL); 
      self.clientCredential = [NSURLCredential credentialWithIdentity:clientIdentity certificates:(NSArray*)certsArray 
                     persistence:NSURLCredentialPersistenceNone]; 
      CFRelease(certsArray); 
      CFRelease(clientCertificate); 
      CFRelease(result); 
     } 
     else { 
      [self messageBox:@"Warning" : @"Client or Server credentials not found. Server connection may fail"]; 
     } 

     NSDictionary* serverCertQuery = [NSDictionary dictionaryWithObjectsAndKeys: 
             kServerCertificateLabel,   kSecAttrLabel, 
             (id)kSecClassCertificate,  kSecClass, 
             kCFBooleanTrue,     kSecReturnRef, 
             kSecMatchLimitAll,    kSecMatchLimit, 
             nil]; 
     CFArrayRef result1 = nil; 
     err = SecItemCopyMatching((CFDictionaryRef)serverCertQuery, (CFTypeRef*)&result1); 
     if (err == errSecItemNotFound) { 
      [self messageBox:@"Warning" : @"Server certificate not found. Server connection may fail"]; 
     } 
     else if (err == noErr && result1 != nil) { 

      SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(result1, 0); 
      CFDataRef certRefData = SecCertificateCopyData(certRef); 
      self.serverCertificateData = (NSData *)certRefData; 
      CFRelease(certRefData); 
      CFRelease(result1); 
     } 
     else { 
      [self messageBox:@"Warning" : @"Client or Server credentials not found. Server connection may fail"]; 
     } 
    } 
} 

5.验证服务器证书,并返回客户端凭证

胡男孩。这是一个解释如何实际使用检索到的证书的编辑(它应该是很容易理解的部分......)

首先,Apple已经可疑的文档已被新的应用程序传输安全框架废弃(例如,请参阅:http://useyourloaf.com/blog/app-transport-security/)。我不打算在这里介绍它,但我们的想法是强制每个人默认使用https和可信任的证书。对于我的情况下,带证书钉扎和专用客户端,你可以放心地通过添加字典到你的plist关闭此功能的私人服务器之间的相互认证如下:

enter image description here

接下来,在步骤4,你已经有客户端凭证立即响应该挑战,但服务器证书作为由SecCertificateCopyData创建的DER格式的NSData浮动,并且不清楚该挑战到达时应发生什么。

原来你应该做的是在“X.509标准”(https://tools.ietf.org/html/rfc5280)的第6节中实现算法。幸运的是,这是由iOS SecTrustEvaluate函数在幕后实现的,但是有脚手架来构建和理解奇怪的东西。

[轻微的问题 - 空间不足!增加了一个新的问题,包括这个步骤结束]

https://*.com/questions/35964632/correctly-use-a-pinned-self-signed-certificate-in-ios-9-2

[从其他岗位继续]

所以这是它。对不起生产质量不太好,但我想把它放在一起,而它仍然在我心中。如果我发现错误,我会更新该帖子。

希望这有助于和这里的最后一个环节,以一个非常好的书,除其他事项外,会给你信任的商业CA毛骨悚然......

https://www.cs.auckland.ac.nz/~pgut001/pubs/book.pdf

+0

嗨,老兄真棒后,你后续但是后来的帖子被删除了。任何你可以在闲谈中提供余下的方式? – murphguy

+0

不幸的是有人低估了它:“我投票结束这个问题作为题外话,因为这是一篇博文,而不是一个问题 - Paulw11 3月13 '16在1:14”。也许有人有能力重新激活它,它仍然存在? – saminpa

+0

谢谢,顺便说一下。我很乐意发送其余的内容,但在这个论坛上没有办法让PM – saminpa