I want to prevent my app/server communication from a MITM attack so I am trying to setup SSL pinning, but I am having problems getting it working with AFNetworking 2.2, using a self-signed certificate. I think it's mostly a problem with how I am generating the certificate.
I first tried generating a self-signed certificate according to these instructions:
Generating the private key:
sudo openssl genrsa -des3 -out server.key 2048
Generating the Signing Request, and using the domain name when asked for the Common Name:
sudo openssl req -new -key server.key -out server.csr
Generating the certificate:
sudo openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
Finally, converting it to der
format (since AFNetworking requires it)
sudo openssl x509 -outform der -in server.crt -out server.der
The server is Ubuntu 12.04, running ngninx+passenger to serve up a Rails 4 app. Here is the bit of my nginx server config to turn on SSL:
server {
listen 80;
listen 443;
server_name myapp.com;
passenger_enabled on;
root /var/www/myapp/current/public;
rails_env production;
ssl on;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
}
After restarting nginx, downloading the der
file, adding it to my project, and renaming it "server.cer" (since AFNetworking requires the certificate to use the .cer extension), I use this code to turn on SSL pinning for my AFHTTPSessionManager
subclass:
client.securityPolicy = [AFSecurityPolicy
policyWithPinningMode:AFSSLPinningModeCertificate];
Then, with the first request to the server AFNetworking attempts to verify that the "trust is valid in the AFServerTrustIsValid
function:
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
SecTrustResultType result = 0;
OSStatus status = SecTrustEvaluate(serverTrust, &result);
NSCAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status);
return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
}
If I put a breakpoint at the return, I can see that the result is always kSecTrustResultRecoverableTrustFailure
.
If I skip the AFServerTrustIsValid
function by setting allowInvalidCertificates
to YES
on the security policy, then the request succeeds. But I don't really want to allow invalid certificates if I don't have to.
Back to the drawing board, this SO question lead me to this tutorial on creating a self-signed cert with also creating a CA. I setup my openssl.cnf file like so:
[ req ]
default_md = sha1
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
countryName = United Kingdon
countryName_default = UK
countryName_min = 2
countryName_max = 2
localityName = Locality
localityName_default = London
organizationName = Organization
organizationName_default = Eric Organization
commonName = Common Name
commonName_max = 64
[ certauth ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints = CA:true
crlDistributionPoints = @crl
[ server ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
nsCertType = server
subjectAltName = DNS:myapp.com
crlDistributionPoints = @crl
[ crl ]
URI=http://testca.local/ca.crl
And then used these commands to generate everything. First the CA stuff:
sudo openssl req -config ./openssl.cnf -newkey rsa:2048 -nodes -keyform PEM -keyout ca.key -x509 -days 3650 -extensions certauth -outform PEM -out ca.cer
Then again the server's private key:
sudo openssl genrsa -out server.key 2048
The signing request:
sudo openssl req -config ./openssl.cnf -new -key server.key -out server.req
The certificate:
sudo openssl x509 -req -in server.req -CA ca.cer -CAkey ca.key -set_serial 100 -extfile openssl.cnf -extensions server -days 365 -outform PEM -out server.cer
And finally the der
file:
sudo openssl x509 -outform der -in server.cer -out stopcastapp.com.der
When I update and restart nginx,, download and add the server.der
to my project (making sure to rename it to server.cer
and to reset the Simulator), I get the same exact result.
The dreaded kSecTrustResultRecoverableTrustFailure
rears its ugly head again.
What am I doing wrong? Am I like WAY off on how this all works, or do I need to tweak just one little thing to get it all working? If you could help in any way I would really, really appreciate it (I've been on this problem for two days now). Thanks!