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:
SSL Pinning can be achieved by certificate pinning only, then why doesn't it work?
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:
Converting certificates into NSData and compare -- didn't work.
Converting certificate into NSData and then to Base64String -- did not work ..
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?