I keep seeing disjointed accounts all over the web of what the .cer file is and how to generate it so that the app can properly communicate with the server via https.
To quote this source, one person says:
1) You now need .cer files in your bundle for all certificates in the chain. So, at least, two certificate files must be present (your certificate and the root CA). In our case, we needed an intermediate as well.
2) These certificates must be in a particular order. Specifically, the certificate that certifies the host that you are communicating with must be first in the list of pinned certificates/keys. It doesn't look like the order of the rest of them matter. This is because AFSecurityPolicy uses the first object in the array to validate the host name.
3) The host validation fails for wildcard certificates if you are communicating with the root domain. For example, consider a certificate pointed at *.foo.com. If you are communicating with foo.com, the host check will fail because the host component counts will not match. I understand the need for this early optimization, but it fails before it even reaches the logic that specifically handles wildcard domains.
Official documentation backs that up here.
I suppose that in order to generate this, one must cat the contents of their whole cert chain into a .cer file in a specific order. I remember seeing this posted somewhere, but can't seem to find it.
The question is, what is the undebatable method of creating a .cer file for AFNetworking?
UPDATE:
After more research, it seems to me that you simply take the .crt file and perform this on it:
openssl x509 -in www.mydomain.com.crt -out www.mydomain.com.cer -outform der
However, even after doing this and attaching the .cer to my app bundle, I get an error here:
+ (NSArray *)pinnedPublicKeys {
static NSArray *_pinnedPublicKeys = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSArray *pinnedCertificates = [self pinnedCertificates];
NSMutableArray *publicKeys = [NSMutableArray arrayWithCapacity:[pinnedCertificates count]];
for (NSData *data in pinnedCertificates) {
SecCertificateRef allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)data);
NSParameterAssert(allowedCertificate);
The error comes in at the last line. I suppose that in the preceding line where it attempts to assign an allowedCertificate
, SecCertificateCreateWithData
has the following description:
/*!
@function SecCertificateCreateWithData
@abstract Create a certificate given it's DER representation as a CFData.
@param allocator CFAllocator to allocate the certificate with.
@param certificate DER encoded X.509 certificate.
@result Return NULL if the passed-in data is not a valid DER-encoded
X.509 certificate, return a SecCertificateRef otherwise.
*/
Clearly it is returning a NULL, but I don't know why. My specific certificate is in the correct format. However, I did notice that there were other 'certificates' in the pinnedCertificates
array, but I have no idea where it is getting them. Nor can I seem to find them or print them out. As far as I know, I only have the one in the app bundle, but it seems to show more than that.
The error generated from the assert on the last line is:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: allowedCertificate'
Assuming you are using AFNetworking 2.5 the steps described below:
Create .cer file
openssl x509 -in www_yourdomain_com.crt -out www_yourdomain_com.cer -outform der
Add .cer file to app project. IMPORTANT: Never add private key to your project!!!
Setup AFNetworking secure policy
Set policy on network manager when making network call
There's no need to manually load .cer file, if you're using
policyWithPinningMode:pinningMode
method to create security policy AFNetworking automatically pins them.