How-to sign an iOS configuration profile generated programmatically?

4.2k views Asked by At

Context

I have a webapp (frontend JS / backend PHP) that generates some MDM iOS configuration profiles (*.mobileconfig) programmatically.

Website users enter some informations, call my PHP api, and my PHP backend generates a configuration profile "on-the-fly" with the user-specific data, save it on the server, and return back the URL of the generated profile, so the user can click this link and install it on its iOS device.

In short: this profile contains in its payload only a webclip (safari shortcut).

Everything works fine, the configuration profile link opens the iOS Settings app that asks the user to install this profile on its device.

My problem is that this programmatically generated profile is not signed. So the user is warned by iOS that the profile is not signed and he must do several additional actions to confirm the profile installation.

I would like that the generated profiles to be signed, so the user can install them more easily and quickly.

Questions

  • is it possible?
  • if yes, is it possible with PHP?
  • if yes, how can I do that?

I read some ressources about signing configuration profiles, by I don't understant everything, I din't have any skills about signing, certificates etc.

That's not clear for me!

Any help appreciated, thanks in advance!

3

There are 3 answers

4
zvi On BEST ANSWER

Yes you can. Also with PHP.

How?

  1. Save the profile you want to sign to a temp file:

    file_put_contents ($tmp_file_name, $profile_data);

  2. Sign the file you have just created:

    $data = shell_exec ("openssl smime -sign -in $tmp_file_name {add here another parameters you need...}");

  3. Send the data to the client:

    echo $data;

  4. Delete the tmp file...

    unlink ($tmp_file_name);

0
Jason C. On

If just for sign issue, I've answered one before, see: https://stackoverflow.com/a/56917093/9297400

/**
 * Sign MobileConfig
 *
 * @return string
 */
function signMobileConfig (
    string $file_full_pathname,
    string $certificate_pathname,
    string $private_key_pathname,
    bool $remove_file = true
) {
    openssl_pkcs7_sign(
        $file_full_pathname,
        $file_full_pathname.'.sig',
        file_get_contents($certificate_pathname),
        file_get_contents($private_key_pathname),
        [], 0
    );

    $signed = file_get_contents($file_full_pathname.'.sig');

    if ($remove_file) {
        unlink($file_full_pathname.'.sig');
        unlink($file_full_pathname);
    }

    $trimmed = preg_replace('/(.+\n)+\n/', '', $signed, 1);
    return base64_decode($trimmed);
}

result of signed config file ( <10 reputation, so click to see result)

Feel free to modify the code above to fulfill your demands.

0
TooLiPHoNe.NeT On

I can confirm that it works using the @zvi answer. Thanks!

Let me give more details about my implementation to help other people. Especially I was confused by which files I need to sign and how to get them.

1. Get a certificate

I used a free certificate from Let's Encrypt, through ZeroSSL to get easily the 2 files needed (certificate and private key).

You'll need to proove that the domain is yours (using FTP or DNS method)

I saved the certificate as "certificate.crt" and the private key as "private-key.pem". Upload them somewhere to your server.

2. Get the certificate authority chain file

If you just use the 2 files above, your profile will be signed bu not "verified" in iOS (the green check sign I was looking for)

Download the "Chain of Trust" from this page.

I used the "Let’s Encrypt Authority X3 (IdenTrust cross-signed)" one, and saved it to "ca-chain.pem". Upload it to your server with the two other ones.

3. Make a PHP signin method

To sign it through a PHP script, I made a method for that, taking an input profile file (not signed) and writing to an output profile file (signed).

I used the method descibed here to call the openssl command from PHP using shell_exec

<?php
function sslSignProfileToFile($inputFile, $outputFile)
{
    $sslPath = '/absolute/path/to/your/cert/files';
    shell_exec("openssl smime -sign -signer ".$sslPath."/certificate.crt -inkey ".$sslPath."/private-key.pem -certfile ".$sslPath."/ca-chain.pem -nodetach -outform der -in ".$inputFile." -out ".$outputFile);
}
?>

4. Generate your profile and sign it

Generate it as you need, and then call the method above to make a signed copy of it.

<?php
sslSignProfileToFile( "/path/to/your/unsigned-profile.mobileconfig", "/path/to/your/signed-profile.mobileconfig");
?>

5. ENJOY!

Then it's up to you :

  • if you are in an AJAX request like me, you can return back in the body the URL to the signed profile to download

  • if you were using a classic form POST action, you can just do a header redirect like explained here (not tested by myself)