Series intro: Apple Pay Certificate Signing Using .NET
Apple’s guide for configuration has two sections, one for “payment processing certificate” and one for “merchant identity certificate.” Both basically use the same steps: You generate a CSR (Certificate Signing Request) using the Keychain Access application on some Mac, upload it to Apple, and then download a signed certificate. To really make it useful you have to take that signed certificate and combine it with your private key, but, strangely, that isn’t mentioned.
What is a CSR? Certificates are really just an asymmetric key bundled with some metadata. Asymmetric algorithms have a public and private component. Public keys are given out, private keys are protected. The metadata tells us things like who the certificate belongs to, what it can be used for, and when it expires (among other things). You can self-sign a certificate, but (usually) some kind of trustworthy authority signs them. This establishes a trust chain and gives us some other things like a revocation mechanism. So we need to send our certificate off to be signed, but we don’t want to send out our private key (because it’s a secret only we should know). This is where CSRs come in. It’s how we transmit the details of our certificate-to-be so that Apple can sign it, but without letting them in on our special little secret (private key).
Anyway the two certificate types Apple calls out are used for different parts of the Apple Pay flow. The “merchant identity” certificate is used by the website hosting the Apple Pay button. In our case, this is really our customer’s job, but you’ll likely want to implement a demo site for you and your test team to retrieve blobs. These “merchant” certificates are the more standard RSA 2048-bit variety. The “payment processing” certificate is used by whoever is going to actually process the Apple Pay transactions. For the purposes of this blog series, that’s us (more or less). Because you use the “payment processing” certificate to derive the key material (using ECDH), they are of the ECC 256-bit variety, which is much rarer. For more details on that decryption process, check out: Decrypting Apple Pay Payment Blob Using .NET – Part 3: Restore the symmetric key.
The first thing I did was use a Mac to make a couple CSRs and then attempt to figure out what’s going on with them. Here’s how one looks:
-----BEGIN CERTIFICATE REQUEST----- MIIBIDCBxQIBADBjMScwJQYJKoZIhvcNAQkBFhhCbGFuY2hAbm90YXJlYWxlbWFp bC5jb20xKzApBgNVBAMMIkFwcGxlIFBheW1lbnQgUHJvY2Vzc2luZyBDZXJ0IFRl c3QxCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhXMw3Nnz XsKqmX4p0CnYmaujpOHuHHxdzSlQmqpYNEXbKMGuAchqVb0ZUHb3SWGDob+F30fE a6mjevMRpWzZwKAAMAwGCCqGSM49BAMCBQADSAAwRQIhALRjcT7HPme46mXJ6TCW 0xshOPyPudXdYsKf8MJ0HO0YAiBREknXnulhzGroP8kuU9j0APvVZgu2WErEXeTd H6Mx4A== -----END CERTIFICATE REQUEST-----
Good news! That looks like a standard PEM-encoded DER ASN.1 CSR. Lot’s of tools out there to look at the content. Try pasting it into Cert Logik’s decoder. Here’s how it looks:
Everything Apple is doing is standard, which is great for our purposes. All we have to do is spit these out using Windows / .NET.
The CertificateRequest class makes this fairly trivial. Here are a couple methods for doing what we need:
public static (byte[] PrivateKey, byte[] CSR) GenerateMerchantIdentityCertificateSigningRequest(string emailAddress, string commonName) { using RSA RSA = new RSACng(2048); CertificateRequest CertificateRequest = new CertificateRequest( $"E={emailAddress}, CN={commonName}, O=US", RSA, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return ( RSA.ExportRSAPrivateKey(), CertificateRequest.CreateSigningRequest() ); } public static (byte[] PrivateKey, byte[] CSR) GeneratePaymentProcessingCertificateSigningRequest(string emailAddress, string commonName) { using ECDsa ECDsa = new ECDsaCng(256); CertificateRequest CertificateRequest = new CertificateRequest( $"E={emailAddress}, CN={commonName}, O=US", ECDsa, HashAlgorithmName.SHA256); return ( ECDsa.ExportECPrivateKey(), CertificateRequest.CreateSigningRequest() ); }
Those are a bit contrived, my apologies. I wanted to call out that you need to save the private key off somewhere because we’ll need it later. The byte[] CSR returned on the Tuple is the DER ASN.1 CSR. Convert that to PEM, write out a file, and you are good to go. If you need help with the PEM part, there is some example code on the doc doing just that. Upload your file to Apple using the developer portal and download your signed certificate. WARNING: Do NOT use Edge. The upload part only works using Chrome. That cost me the better part of a day trying to figure out.
Next time we’ll see how to take the signed certificate and combine it with the private key we stored off.