I am in the process of trying to develop a really simple proof-of-concept iOS MDM that will allow for OTA enrollment of iOS devices. This isn't meant to be some MobileIron replacement or anything. It's really just a learning exercise. I am developing my code in Java, with JAX-RS for the RESTful service endpoints.
At this point, I am able to it a URL (http://myhost/enroll) from my iOS that will return an enrollment response to kickoff the device enrollment process. The response returned looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Inc//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<dict>
<key>URL</key>
<string>https://myhost/profile</string>
<key>DeviceAttributes</key>
<array>
<string>UDID</string>
<string>IMEI</string>
<string>ICCID</string>
<string>VERSION</string>
<string>PRODUCT</string>
</array>
<key>Challenge</key>
<string>MySuperSecureChallenge</string>
</dict>
<key>PayloadOrganization</key>
<string>Example Inc.</string>
<key>PayloadDisplayName</key>
<string>Profile Service</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadUUID</key>
<string>fdb376e5-b5bb-4d8c-829e-e90865f990c9</string>
<key>PayloadIdentifier</key>
<string>com.example.mobileconfig.profile-service</string>
<key>PayloadDescription</key>
<string>Enter device into the Example Inc encrypted profile service</string>
<key>PayloadType</key>
<string>Profile Service</string>
</dict>
</plist>
Once my device receives this payload, it takes me to the Settings app and prompts me to install the profile. When I click "Install", it performs an HTTP POST
to another URL (http://myhost/profile) and includes the attributes I requested in the response from /enroll.
My /profile
endpoint is able to successfully extract the signed response from my iOS device, which looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CHALLENGE</key>
<string>MySuperSecureChallenge</string>
<key>IMEI</key>
<string>__MY_IMEI__</string>
<key>PRODUCT</key>
<string>iPhone10,4</string>
<key>UDID</key>
<string>__MY_UDID__</string>
<key>VERSION</key>
<string>15B202</string>
</dict>
</plist>
My question is: now what?? I've found example payloads like this (from Apple) that show how to do SCEP enrollment. However, I don't want to do SCEP for this simple proof-of-concept. Is there something else I can return at this stage? If so, what should the response contain and look like? I haven't found any documentation outlining what should be returned by my /profile
endpoint if I'm not using SCEP, so I'm a little stuck.
UPDATE: I found some resources online that imply that I need to either use SCEP or provide a PKCS#12-formatted certificate in the payload. So, I have updated the return value of the /profile
to look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Inc//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadContent</key>
<dict>
<key>Challenge</key>
<string>MyChallengeGoesHere</string>
</dict>
<key>PayloadDescription</key>
<string>Provides device encryption identity</string>
<key>PayloadUUID</key>
<string>fd8a6b9e-0fed-406f-9571-8ec98722b713</string>
<key>PayloadType</key>
<string>com.apple.security.pkcs12</string>
<key>PayloadDisplayName</key>
<string>Cert Test</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadOrganization</key>
<string>Example, Inc.</string>
<key>PayloadIdentifier</key>
<string>com.example.profileservice.webclip</string>
<key>PayloadContent</key>
<string>__MY_BASE64_ENCODED_PKCS12_CERT__</string>
<key>Password</key>
<string>__MY_CERT_PASSWORD__</string>
</dict>
</array>
</dict>
</plist>
I'm returning this value back to the client, but when I click Install
on my iOS device, I get an error saying:
Profile Installation Failed A connection to the server could not be established.
Now, I know that communication isn't an issue, because I see the connection to /profile
in my logs and debugger. Is there something else I'm missing? Does the profile need to be digitally signed? If so, what format is it? Which certificate should I be using to sign it?
After LOTS of trial and error, I have finally figured out what the issue was for this profile. I was missing a couple of parameters: PayloadIdentifier
and PayloadUUID
. Further, the value for the PayloadContent
parameter containing my base64-encoded certificate should be data
, not string
. So, my new profile looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Inc//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>9f93912b-5fd2-4455-99fd-13b9a47b4581</string>
<key>PayloadIdentifier</key>
<string>org.example.mymdm</string>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadType</key>
<string>com.apple.security.pkcs12</string>
<key>PayloadUUID</key>
<string>f78c5002-3907-4f67-b631-d41c44283628</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadIdentifier</key>
<string>com.mymdm.cert</string>
<key>Password</key>
<string>__MY_CERT_PASSWORD__</string>
<key>PayloadContent</key>
<data>__MY_BASE64_ENCODED_P12__</data>
</dict>
</array>
</dict>
</plist>
Once I made these changes, I was able to successfully install the profile on my iOS device.