iOS - how do I get server certificate into iOS client keychain for pinning - ios

TL;DR version: Is there any way to pass a Server certificate to an iOS client that doesn't involve also passing along the Server's private key?
I have written an iOS client app that communicates with my macOS server app (so I have control over both ends). I have implemented certificate pinning using a self-signed certificate to make things more secure. To accomplish this during development, I hardcoded the Server cert into the iOS client app and told the client to only connect to a server that gives you that exact cert during the TLS handshake. Everything is working great.
However in the real world I am selling this system as a set (1 Server, multiple clients to each customer), so I cannot hardcode a Server cert into the iOS client. My plan is to instead deliver the Server cert out of band (via email) to the iOS client like mentioned here: Making Certificates and Keys Available To Your App:
Apps can only access keychain items in their own keychain access groups.
To use digital identities in your own apps, you will need to write code to import them. This typically means reading in a PKCS#12-formatted blob and then importing the contents of the blob into the app's keychain using the function SecPKCS12Import
One way to provision an identity is via email. When you provision a device, send the associated user an email with their client identity attached as a PKCS#12 file.
My problem is that a .p12 file contains the certificate and the private key of the server - this seems very wrong to pass the private key along as well.
Is there any other way to pass the Server certificate to the iOS client that doesn't involve also passing along the Server's private key?
Thanks!!!

I was overthinking things here, the solution is actually pretty simple.
I just needed to email the Server's public certificate out of band to the client device with a custom extension like cert.myCustomExt1234. This is because the .crt extension is already claimed by iOS so you have to register your app to handle custom extensions (see apple docs here). Then in my app I can do all the logic of cert pinning using that out of band delivered Server public cert.
The key was changing the file extension to something not already claimed by iOS.

Related

With iOS push certificates, why does having an SSL certificate allow Apple to know that its your server they're connecting with?

I'm reading this article on iOS push certificates, and I'm confused about this paragraph:
Your backend sends notifications through Apple's servers to your application. To ensure that unwanted parties are not sending notifications to your application, Apple needs to know that only your servers can connect with theirs. Apple therefore requires you to create an SSL certificate to be able to send push notifications.
My understanding of SSL certificates is that if a server has one, that server is able to encrypt data that it sends to a device. But it says here Apple needs to know that only your servers can connect with theirs. I don't understand how having an SSL certificate ensures that. Does anyone have any insight?
The article shouldn't have used the term SSL Certificate. SSL is the Secure Sockets Layer (which was superseded by TLS many years ago). SSL and TLS define the handshake that is used to negotiate encryption on a connection.
Enabling SSL on a web server required you to have a certificate to verify your server's identity and so this became known colloquially as an "SSL certificate".
While it isn't often used on the web, in SSL/TLS both parties can present a certificate so that there is mutual authentication.
What you typically have is actually an x.509 certificate. This is the case with the push notification service.
An x.509 certificate contains some information including the identity of the certificate holder, their private key and a signature from a trusted party that can be used to verify the information.
For push notifications, the developer generates a certificate request and submits this to Apple who sign it with their private key. Apple is the trusted party in this case.
When this certificate is subsequently presented to Apple's server they can verify that signature using their public key to confirm the identity of the connecting party.
You have has encrypted the message with their private key (Apple can decrypt it with the public key included in the certificate).
What this means is, that as long as the developer has kept their private key secure (which is why you wouldn't connect directly to the push service from your app, for example) then Apple can be sure of the identity of the server making the connection.
If someone was trying to impersonate your server then, as long as you have kept your private key secure, they can't encrypt the data properly. If they use a forged certificate that uses a public/private key pair known to them then the signature on the certificate won't be valid and Apple will reject it.

Convert PEM Response to PCKS12 Data in Swift

We are using AWS iOS SDK for an IoT project in our company. It is a simple application for now. User logs in to our system and tries to connect to the AWS IoT service.
https://github.com/awslabs/aws-sdk-ios-samples/tree/master/IoT-Sample/Swift/IoTSampleSwift
In the sample project above, application generates its own certificate and key pair using AWSIoT SDK. However, in our case, we don't want to generate certificates like this.
We created a Lambda Function on the server side to generate certificates for our users. iOS application requests certificate and as a response server sends a key pair, certificatePem, certificateId and certificate Arn. All of the components are created by AWSIoT SDK, if we use AWSIoTManager.defaultManager(). createKeysAndCertificateFromCsr method.
In this case, we need to import our user identity to the iOS keychain. AWS SDK has another method to handle this problem:
AWSIoTManager.importIdentityFromPKCS12Data method
We need to convert the server response to PKCS12Data to use the generated certificate. How can we do this?
Thanks in advance.
If you've got the key pair, the certificate PEM, certificate ID, and the certificate ARN, you can use the AWS Mobile SDK for iOS's IoT classes as a reference while implementing code for importing these into the keychain. The AWSIoTKeychain class contains methods for importing keys, and the AWSIoTManager -createKeysAndCertificateFromCsr: method imports the certificate into the keychain. Let us know if that information helps, and thanks for using AWS IoT.

Validating requests in /server url

In iOS MDM /server url will be called for each operation by the device when it is woken by APNS. I have securely encrypted and signed other profiles at the time of enrollment and successfully passed the server url to device. Its working fine but I have few concerns over this server endpoint as follows.
1) Any client or entity who could send similar plist payload can invoke this service. If a 3rd party has access to a device UDID they can compose this xml payload and invoke this service. From the server point of view it will be hard to track this behavior and identify real devices. To identify that in the real scenario will it send and CMS data or related to validate this scenario?
2) Once the device hit this endpoint from server we can generate operation profiles and send back to devices. For the profiles at the enrollment time we could extract the public certificate from CMS data and encrypt from that. But for this server url how do I achieve that? Seems its not getting any cert like that from device side. Just wondering whether to save the public keys we got in earlier stages but since at the enrollment it goes through 2 SCEP calls not sure what to use it. Will those subsequent profiles payload can be encrypted using previous public cert? Right now I do the signing anyway which works fine.
1.) Any client or entity who could send similar plist payload can invoke this service. If a 3rd party has access to a device UDID they can compose this xml payload and invoke this service. From the server point of view it will be hard to track this behavior and identify real devices. To identify that in the real scenario will it send and CMS data or related to validate this scenario?
Yes, Any client who could possess the UDID and Server URl can send a valid Plist to your server acting like the device.
But they cannot sign the plist with the private key in the device(Which is generated during SCEP enrolment). You would be having corresponding Public key for it to validate the signature.
To force the device to send the signature along each request to Server URL, you have to include SignMessage tag in your MDM payload and set it as true. Like this
<key>SignMessage</key>
<true/>
So when you include this tag along with your MDM payload, you would be get the signature of Identity Private key in the Header HTTP_MDM_SIGNATURE.
Then you can validate the signature using your public key.
2.) Just wondering whether to save the public keys we got in earlier stages but since at the enrollment it goes through 2 SCEP calls not sure what to use it.
Yes I mentioned in the previous answer you should save the public certificate which is issued during SCEP phase. Later you will use that public certificate to Validate the signature from Device and Encrypt the profile you are sending.
Regarding 2 SCEP calls, First SCEP call is to generate the certificate and securely transfer the MDM Payload and actual SCEP payload which will be used as Idenitity certificate for MDM.
So you should use the second one for validating the signature and encryption.
One more hint is, you would have mentioned IdentityCertificateUUID in your MDM payload. The Identity Certificate SCEP payload should have same UUID as its PayloadUUID . That SCEP payload's certificate will be used as the identity certificate for MDM.
Ok. The bottom line that you want to authenticate device.
Each device has an identity cert (a cert distributed in PKCS12 or through SCEP).
Each time when a device communicate to the server it does authentication using SSL client certs.
Most of the time there is a reverse proxy sitting upfront of your web server. It could be Apache or Nginx or anything else. This reverse proxy terminates SSL connection and checks client certificate. Usually, they are configured to pass this client certificate as a header to your web application.
This way your web app can get this header, get a certificate out of it and check against your DB whether a device with specific udid (passed to your endpoint) have a certificate (passed to your webapp in the header).
I am not sure which reverse do you use and whether it's configured properly to pass the certificate.

Retrieve Client ios app certificate

I want to proxy traffic from an ios application to Fiddler (or Burp). It looks like the application sends a client certificate to the server.
I will need to retrieve this cert from the phone(it's jailbroken) and import it to my proxy. Is there a way to do that ?
The client certificate is used to identify the client. If the programmer of the app made his job well, you will face difficulties (hopefully). Likely, and most secure, the private key and identity resides in the key-chain. Less secure, it resides in a secured archive (.p12, .pkcs12, .pfx) in the bundle, whose password resides in the key-chain.
If the programmer did his job not so well, you might find the password of the secured archive in the clear somewhere in the apps binary (there're actually floating samples around which do exactly this).

ios generate application specific key

I'm working on an ios application without authentication. Now I would like to protect my server API from calls other then my ios application. A possible solution would be to have the application generate a unique key (based on the appname and the signing), which is not stored on the device since this is the main problem. I could think off an application logic that does some protection combined with some file encryption but the problem is that somewhere something is stored (ex public key can be stored in keychain but still not safe for my API-hackers).
Anyone any tips/advice on how I can handle this ?
thanks in advance
In short, there is no 100% secure way to make sure that the request comes from your application, if the key is available to the iPhone, it's available to extract from the iPhone.
You can make it reasonably safe by calculating a key runtime from info in the application as you say and communicate it over SSL, but a determined attacker can always reverse engineer the key generation too.
What you want to do is employ mutually-authenticated SSL, so that your server will only accept incoming connections from your app and your app will only communicate with your server.
Here's the high-level approach. Create a self-signed server SSL certificate and deploy on your web server. You can use freely available tools for this, like keytool, and I think (but don't know for sure) that Apple includes a tool for this with the iOS SDK. Then create a self-signed client and deploy that within your application in a custom keystore included in your application as a resource. Configure the server to require client-side SSL authentication and to only accept the client certificate you generated. Configure the client to use that client-side certificate to identify itself and only accept the one server-side certificate you installed on your server for that part of it.
If someone/something other than your app attempts to connect to your server, the SSL connection will not be created, as the server will reject incoming SSL connections that do not present the client certificate that you have included in your app.

Resources