1. 程式人生 > >iOS訪問https ssl和tls雙向加密

iOS訪問https ssl和tls雙向加密

由於專案需求,訪問服務是https的,並且使用的是ssl加密方式

下面說明使用MKNetworkit網路庫實現的程式碼:

- (void)testClientCertificate {
  SecIdentityRef identity = NULL;
  SecTrustRef trust = NULL;
  NSString *p12 = [[NSBundle mainBundle] pathForResource:@"testClient" ofType:@"p12"];
  NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
  
  [[self class] extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];
  
  NSString *url = @"https://218.244.131.231/ManicureShop/api/order/pay/%@";
  NSDictionary *dic = @{@"request" : @{
                            @"orderNo" : @"1409282102222110030643",
                            @"type" : @(2)
                            }
                        };
  
  _signString = nil;
  NSData *postData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
  NSString *sign = [self signWithSignKey:@"test" params:dic];
  NSMutableData *body = [postData mutableCopy];
  NSLog(@"%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
  url = [NSString stringWithFormat:url, sign];
  
  MKNetworkEngine *engine = [[MKNetworkEngine alloc] initWithHostName:@"218.244.131.231"];
  MKNetworkOperation *op = [engine operationWithPath:[NSString stringWithFormat:@"/ManicureShop/api/order/pay/%@", sign] params:dic httpMethod:@"POST" ssl:YES];
  op.postDataEncoding = MKNKPostDataEncodingTypeJSON; // 傳JOSN
  
  // 這個是app bundle 路徑下的自簽證書
  op.clientCertificate = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"testClient.p12"];
  // 這個是自簽證書的密碼
  op.clientCertificatePassword = @"testHttps";
  
  // 由於自簽名的證書是需要忽略的,所以這裡需要設定為YES,表示允許
  op.shouldContinueWithInvalidCertificate = YES;
  [op addCompletionHandler:^(MKNetworkOperation *completedOperation) {
    NSLog(@"%@", completedOperation.responseJSON);
  } errorHandler:^(MKNetworkOperation *completedOperation, NSError *error) {
    NSLog(@"%@", [error description]);
  }];
  
  [engine enqueueOperation:op];
  return;
}
// 下面這段程式碼是提取和校驗證書的資料的
+ (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
  OSStatus securityError = errSecSuccess;
  
  // 證書金鑰
  NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"testHttps"
                                                                forKey:(__bridge id)kSecImportExportPassphrase];
  
  CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
  securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
  
  if (securityError == 0) {
    CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
    const void *tempIdentity = NULL;
    tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
    *outIdentity = (SecIdentityRef)tempIdentity;
    const void *tempTrust = NULL;
    tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
    *outTrust = (SecTrustRef)tempTrust;
  } else {
    NSLog(@"Failed with error code %d",(int)securityError);
    return NO;
  }
  return YES;
}


下面說明一下使用AFNetworking網路庫訪問的方式:

- (void)testClientCertificate {
  SecIdentityRef identity = NULL;
  SecTrustRef trust = NULL;
  NSString *p12 = [[NSBundle mainBundle] pathForResource:@"testClient" ofType:@"p12"];
  NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
  
  [[self class] extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];
  
  NSString *url = @"https://218.244.131.231/ManicureShop/api/order/pay/%@";
  NSDictionary *dic = @{@"request" : @{
                            @"orderNo" : @"1409282102222110030643",
                            @"type" : @(2)
                            }
                        };
  
  _signString = nil;
  NSData *postData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
  NSString *sign = [self signWithSignKey:@"test" params:dic];
  NSMutableData *body = [postData mutableCopy];
  NSLog(@"%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
  url = [NSString stringWithFormat:url, sign];
  
  AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
  manager.requestSerializer = [AFJSONRequestSerializer serializer];
  manager.responseSerializer = [AFJSONResponseSerializer serializer];
  [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
  [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
  manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json", @"text/plain"]];
  manager.securityPolicy = [self customSecurityPolicy];
  
  [manager POST:url parameters:dic success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    
    NSLog(@"Error: %@", error);
  }];
}

下面這段程式碼是處理SSL安全性問題的:
/**** SSL Pinning ****/
- (AFSecurityPolicy*)customSecurityPolicy {
  NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"testClient" ofType:@"cer"];
  NSData *certData = [NSData dataWithContentsOfFile:cerPath];
  AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
  [securityPolicy setAllowInvalidCertificates:YES];
  [securityPolicy setPinnedCertificates:@[certData]];
  [securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate];
  /**** SSL Pinning ****/
  return securityPolicy;
}


為了實現訪問https tls加密方式,我也費了不少時間來查,這裡寫下此文章,希望對大家有用!