SSL pinning not working (Objective-C, using NSURLSession)
Asked Answered
J

1

0

NOTE: The same SSL pinning on working on Android counterpart application. So did iOS changed something? Are there many number of certificates on the server for my url and they keep rotating everyday?

Questions are:

  1. SSL Pinning can be achieved by certificate pinning only, then why doesn't it work?

  2. If some one has a clear explanation of how to do it using public key? Please explain.

First I want to explain how did I get local certificate. I think it's pretty straight. I just typed in https://ez-pay.io and then I clicked on the lock icon and downloaded the certificate. If you know what I mean. Now I think this could be the problem too. My question is: Is it the right way to download the certificate?

Ok let's assume this is right. I embedded or copy pasted the certificate into my Xcode project.

Now here is the code to fetch remote (server) certificate and compare it with the local one:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
    NSLog(@"SECURITY : didReceiveChallenge");

    // REMOTE CERTIFICATE
    SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;

    // Evaluate server certificate [Whether the certificate is valid or not] --
    SecTrustResultType result;
    SecTrustEvaluate(serverTrust, &result);
    BOOL isRemoteCertValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);

    // Remote certificate object --
    SecCertificateRef certificateRemote = SecTrustGetCertificateAtIndex(serverTrust, 0);
    // Returns a DER representation of a certificate --
    NSData *remoteCertData = CFBridgingRelease(SecCertificateCopyData(certificateRemote));

    NSData *localCerData = [self GetLocalCertificateData];

    // base 64 encoding of remote certificate --
    NSString* strRemote = [remoteCertData base64EncodedStringWithOptions:0]; // get string from certificate --

    NSString *strLocal = [self  GetLocalCertificateStringForPublicKey];

    // The pinning check -- compare the remote and local certificate data / string --
//    if (isRemoteCertValid && [strLocal isEqualToString:strRemote] )
    if (isRemoteCertValid && [localCerData isEqualToData:remoteCertData] )
    {
        NSLog(@"SECURITY : OK ");
    }
    else
    {
        NSLog(@"SECURITY : FAILS");
        isCertificateValid = false;
    }
}

- (NSData *)GetLocalCertificateData
{
    if ( MODE == PROD_US)
    {
        NSString *pathToCert = [[NSBundle mainBundle]pathForResource:@"prod_ezpay" ofType:@"cer"];
        NSData *localCertificate = [NSData dataWithContentsOfFile:pathToCert];
        return localCertificate;
    }
    else if (MODE == PREP_US)
    {
        NSString *pathToCert = [[NSBundle mainBundle]pathForResource:@"prepEZPAY" ofType:@"cer"];
        NSData *localCertificate = [NSData dataWithContentsOfFile:pathToCert];
        return localCertificate;
    }

    return nil;
}

Now the problem is that comparison of certificates Data always fails:

[localCerData isEqualToData:remoteCertData] // Always false

I have been trying many different approaches checked 5-6 links on google, all have same approach. This project is a bit old, 3-4 years and written in Objective-C.

I tried 3 approaches:

  1. Converting certificates into NSData and compare -- didn't work.

  2. Converting certificate into NSData and then to Base64String -- did not work ..

  3. Also a hack - I printed the data and saved it in string format and hard coded it in the app and then next time onwards keep comparing it with server's certificate data. This approach only works for one day and next day server's certificate data changes. Don't know what is happening.

Please suggest what is wrong in here?

Journalistic answered 12/12, 2019 at 18:26 Comment(9)
"This approach only works for one day and next day server's certificate data changes" the server certificate can change each time you connect to it. What are you really attempting to do, what do you want to compare and why?Cheap
@PatrickMevzek I want to enable SSL pinning in my iOS App. And i am having a hard time pinning a certificate.Also why would certificate change ? If you go to htps://ez-pay.io and download the certificate and check its expiry date , you will see that it will only be changed after a year or so ..Shortly put , I tried most of the possible ways to do SSL pinning in iOS , ObjC and no luck since 10 days .Journalistic
"Also why would certificate change ?" Because they expire, for one. Because behind a single website name you can have a farm of servers and there is absolutely no need for all of them to have the exact same certificate, it just needs to be a valid certificate for the name. Hoping the certificate to stay the same eternally is not going to fly well.Cheap
"you will see that it will only be changed after a year or so " False. It can be changed at any time. The expiration date means it HAS to be changed BEFORE this date because AFTER the date it is not valid anymore. It says nothing about when before expiration it can be changed. It can be changed at any time and any number of times.Cheap
Ok. If I assume that there is a chain of certificate behind the domain name i.e. ez-pay.io still , Above code should work. I have searched almost 10 links on google so far and all suggest the same answer. What do you think is my approach for SSL Pinning in my ios app is correct ?Journalistic
Just turn on certificate transparency, don't need to invent bicycles.Paymaster
What do you mean by certificate transparency ???? @PaymasterJournalistic
developer.apple.com/videos/play/wwdc2016/706Paymaster
your certificates are probably poorly configured for apple's latest TLS restrictions, I have info about this and a package to help with TLS/SSL and iOS written in obj-c here -> github.com/eamonwhiter73/IOSObjCWebSocketsAlwin
J
1
  • I figured out the issue .

    • You must always download ssl certificate when you are out of your local office network or wifi . Because if you download your certificate within vpn / office LAN / office wifi , your certificate will be tempered / overwritten .
    • Thus when you will pin the certificate which you downloaded in your secure private office network then , then it will never match the remote certificate as remote certificate is always public.

      Therefore , download the certificate from open wifi or your own data plan :) and then pin this certificate in your code and i am sure then it will match the remote certificate.

Journalistic answered 23/5, 2020 at 16:24 Comment(2)
Hi @Rakesh , Can you please add final code of SSL pinning authdelegate method in here, even i am facing same problem, not able to figure out.Athena
Can you share final code of your session delegate didRecieveAuth? Seems like you did't call completion handler. Even i am facing the same issue, please help me to figure out. Kindly share you final session delegate method.Athena

© 2022 - 2024 — McMap. All rights reserved.