(Disclaimer: I’m not a security expert – I’m just a dude who bashes his head against the wall until I can find a way to make it work.)
The certificate file that PayPal issues (cert_key_pem.txt) is pretty useless if you’re calling their API from .NET. (I don’t know about other platforms).
Fortunately, it’s a simple one step process to generate a cert_key.der file that you can then use from C# like this:
X509Certificate certificate = X509Certificate.CreateFromCertFile(certificatePath);
What scares me (and thus became the motivation behind the blog post) is that while there is lots of documentation around this process, it’s all painfully over-complicated! Unfortunately this hack method has been distributed so far now that even O’Reilly, a respected publisher, is touting it in print as the best process. Their method is to use OpenSSL to convert it from PEM to P12 format, then import it into your local certificate store using MMC, and finally export it back to CER format.
You can actually achieve an equivalent process using a single call to OpenSSL:
openssl x509 -outform der -in cert_key_pem.txt -out cert_key.der
It puzzles me why this harder, more error prone method is getting all the airtime? I came up with two answers:
- It’ could be a new feature in OpenSSL and hence wasn’t possible before.
- This process is normally being executed by people who’ve never touched X509 certificates before, and thus never realised that DER (what OpenSSL produces) is the same thing as CER (what .NET expects).
I hope people find this simpler method before they start littering their certificates throughout their development machine registries.
Update: The rest of the story …
After playing with this a bit more, I can now see where this idea has come from, however I still don’t like it.
A .cer file only contains your public key, which isn’t very useful for signing something; .NET needs to have access to the private key too. If you only used the code above, then moved you app to a different machine, you’d be getting an SSL-related exception when you called httpWebRequest.GetResponse(). Taking a look at the tracing output from System.Net would reveal something like this:
System.Net Information: 0 :  SecureChannel#23264094 – Locating the private key for the certificate: [Subject]
C=AU, S=New South Wales, L=Neutral Bay, CN=tatham_api1.oddie.com.au
Eemail@example.com, CN=live_camerchapi, OU=live_certs, O=”PayPal, Inc.”, L=San Jose, S=California, C=US
23/05/2007 11:08:48 AM
20/05/2017 11:08:48 AM
System.Net Information: 0 :  SecureChannel#23264094 – Cannot find the certificate in either the LocalMachine store or the CurrentUser store.
Notice that last line saying it couldn’t find the certificate in the store – even though we’d loaded it directly from file? That’s .NET trying to find the full certificate to get the private key.
This is where the first approach comes in handy – in the process using their local certificate store to import/export, most users wouldn’t have deleted the certificate afterwards. The private key remains in the certificate store, and .NET can resolve it from there. There is in fact a benefit to this approach – your private key can be stored somewhere outside you file system and thus be more secure. Well, that’s the theory anyway. In reality – it still needs to be somewhere that your process can access, be it an ACLed file or the certificate store it probably doesn’t make that much of a difference.
The problem to this approach is the deployment impact – you need to be able to install the certificate in the servers store. In some environments this just isn’t possible, and in other environments it just sounds painfully fiddly. So – how can we load the entire certificate, including the public and private key tokens – from a file?
As before, we’re going to use OpenSSL to convert the supplied certificate into something useful. This time though, we’re going to convert it to a P12 file (otherwise known as a “PKCS#12 container file”). These files contain not only certificates, but also private keys in encrypted form.
openssl pkcs12 -export -in cert_key_pem.txt -out cert_key.p12
When you execute this command, OpenSSL will ask you to choose a password. It will use this password to encrypt your private key, so make the password complex and unique.
You can now load and attach the certificate like so:
string certificatePath = Path.GetFullPath(Path.Combine(Assembly.GetExecutingAssembly().Location, @”..\..\..\..\cert_key.p12″));
string certificatePassword = “p12passwordhere”;
X509Certificate clientCertificate = new X509Certificate2(certificatePath, certificatePassword);
Voila! It works.
Now, you just need a way to securely store your API username, API password, P12 file and P12 password. That’s something I’ll cover in another blog post.