说明
这里我们所访问的是微信支付下的企业付款到零钱接口,该接口需要单向认证,我们需要从微信商户后台下载需要的证书(.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
];
NSURLSession
*session
= [NSURLSession sessionWithConfiguration
:config delegate
:self delegateQueue
:[NSOperationQueue mainQueue
]];
NSURLSessionDataTask
*task
= [session dataTaskWithRequest
:request completionHandler
:^(NSData
* _Nullable data
, NSURLResponse
* _Nullable response
, NSError
* _Nullable error
) {
NSLog(@"返回的数据 data:%@",data
);
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
;
}