I got below code for certificate pinning in Android
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
.add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
.add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
.add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
.build();
How do i achieve same task in IOS using NSURLSession method?
Got some reference code here
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"MyLocalCertificate" ofType:@"cer"];
NSData *localCertData = [NSData dataWithContentsOfFile:cerPath];
if ([remoteCertificateData isEqualToData:localCertData]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}
else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
EDIT PART
I got below solution, which delegate function is called automatically in NSURLSession, can anyone explain how it will work ? ALSO Need to send multiplier certificate how do i do it?
(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
NSString *authMethod = [[challenge protectionSpace] authenticationMethod];
if ([authMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
} else {
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"MyLocalCertificate" ofType:@"cer"];
NSData *localCertData = [NSData dataWithContentsOfFile:cerPath];
NSURLCredential *credential;
if ([remoteCertificateData isEqualToData:localCertData]) {
credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}
else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
NSLog(@"Finished Challenge");
}
}
The if block skips certificate pinning if the authentication method is NSURLAuthenticationMethodServerTrust
. I'm not quite sure why you'd do that---you'll have to look at the source where you got this code snippet and see what it's requirements are.
If the authentication method is anything else, the else block does certificate pinning.
The variable serverTrust
is sent to the SSL transaction state from the server. The main thing here is that it has a chain of certificates that authenticate the server. In the next line, certificate
is set to the leaf certificate in the chain, i.e. the server's certificate.
remoteCertificateData
is essentially a big binary blob representing the information in the certificate. The call to CFBridgingRelease
is needed for memory management (all the CFxxx
functions are C/C++ functions, not Objective-C, and the memory management is a little bit more complicated than normal).
localCertData
is a binary blob of the information in the local copy of the certificate. Note that iOS apps are (more or less) a collection of files including the executable as well as various resources, etc. As part of the build process, you would arrange for a copy of the server's certificate to be included in thes collection (NSBundle
) of files. The cerPath
variable is set to the file path of the local copy of the certificate.
Finally, we check to see if the two binary blobs are equal. If not, then the certificate from the server is bogus and we don't proceed with the request.
I'm not completely sure what you mean by "Need to send multiplier certificate". Judging from the Java code you reference I am assuming what you mean is that you want to compare the server certificate with multiple local certificates. In that case, something (roughly) like the following (note: untested code):
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
BOOL match = NO;
NSURLCredential *credential;
for (NSString *path in [[NSBundle mainBundle] pathsForResourcesOfType:@"cer" inDirectory:@"."]) {
NSData *localCertData = [NSData dataWithContentsOfFile:path];
if ([remoteCertificateData isEqualToData:localCertData]) {
credential = [NSURLCredential credentialForTrust:serverTrust];
match = YES;
break;
}
}
if (match) {
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
NSLog(@"Finished Challenge");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With