说明
这里我们所访问的是微信支付下的企业付款到零钱接口,该接口需要单向认证,我们需要从微信商户后台下载需要的证书(.p12格式),然后将证书拖入工程中即可
注意:证书内嵌到app内是不安全的,这里只是演示,实际开发证书应该放服务器(.pm格式)
思路
首先第一步我们需要发起请求,然后这只请求代理,在代理中读取本地证书验证。
请求
NSMutableURLRequest *request=[[NSMutableURLRequest alloc]init];
[request setURL:[NSURL URLWithString:url]];
[request setHTTPMethod:@"POST"];
NSString *contentType=[NSString stringWithFormat:@"text/xml"];
[request addValue:contentType forHTTPHeaderField:@"Content-Type"];
NSMutableData *postBody=[NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:
@"<xml><mch_appid>%@</mch_appid><mchid>%@</mchid><nonce_str>%@</nonce_str><partner_trade_no>%@</partner_trade_no><openid>%@</openid><check_name>%@</check_name><amount>%d</amount><desc>%@</desc><sign>%@</sign></xml>",self.mch_appid,self.mchid,self.nonce_str,self.partner_trade_no,self.openid,self.check_name,self.amount,self.desc,self.sign] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postBody];
NSString *bodyStr=[[NSString alloc]initWithData:postBody encoding:NSUTF8StringEncoding];
NSLog(@"********提交表单信息 post body: %@",bodyStr);
NSURLSessionConfiguration * config = [NSURLSessionConfiguration defaultSessionConfiguration];
//config.TLSMaximumSupportedProtocol = kTLSProtocol1;
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//NSURLSession *session=[NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"返回的数据 data:%@",data);
// NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
if(error)
{ NSLog(@"error:%@",error.description);
return;
}
}];
[task resume];
这里我们发起的请求时POST,请求的参数是xml格式的,NSURLSession的代理(delegate)设置self,因为self实现了NSURLSessionDelegate接口
@interface HttpUtils:NSObject<NSURLSessionDelegate>
代理
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
NSLog(@"证书认证");
NSLog(@"didReceiveChallenge: %@", challenge.protectionSpace.authenticationMethod);
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
NSLog(@"challenged for client certificate");
NSString *sslCertName = @"apiclient_cert";
NSString *path2 = [[NSBundle mainBundle] pathForResource:sslCertName ofType:@"p12"];
NSData *p12data = [NSData dataWithContentsOfFile:path2];
CFDataRef inP12data = (__bridge CFDataRef) p12data;
SecIdentityRef myIdentity;
SecTrustRef myTrust;
extractIdentityAndTrust(inP12data, &myIdentity, &myTrust);
SecCertificateRef myCertificate;
SecIdentityCopyCertificate(myIdentity, &myCertificate);
const void *certs[] = {myCertificate};
CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);
CFRelease(myCertificate);
secureCredential = [NSURLCredential credentialWithIdentity:myIdentity
certificates:(__bridge NSArray *) certsArray
persistence:NSURLCredentialPersistencePermanent];
CFRelease(certsArray);
[[challenge sender] useCredential:secureCredential forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential, secureCredential);
}
else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSLog(@"challenged for server trust");
[challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]
forAuthenticationChallenge: challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,
[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
} else {
NSLog(@"other challenge");
if ([challenge previousFailureCount] == 0) {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
} else {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,nil);
}
}
}
OSStatus extractIdentityAndTrust(CFDataRef inP12data, SecIdentityRef *identity, SecTrustRef *trust)
{
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("1577168641");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inP12data, options, &items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = (CFDictionaryRef)CFArrayGetValueAtIndex(items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
*trust = (SecTrustRef)tempTrust;
}
if (options) {
CFRelease(options);
}
return securityError;
}