Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Certificate pinning in Xcode

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");
    }
}
like image 330
9to5ios Avatar asked Dec 21 '16 10:12

9to5ios


1 Answers

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");
like image 116
Matthew Burke Avatar answered Oct 23 '22 12:10

Matthew Burke