With Apple’s frequent cancellation of corporate certificates and difficult application requirements for enterprises, the proxy signature industry has made use of Apple’s ad-hoc certificate device to come up with an alternative, the so-called super signature.


From the point of view of the whole installation process, super signature does not need to click trust like enterprise certificate, and the experience is simpler and easier to accept than enterprise distribution.


Take the super signature price of xx platform on the market now:


That said, supersignatures now cost up to 22 yuan per device, which is a bit high and makes you wonder where the gadget came from.


What did he use to create the supersignature from Apple?


First, Apple has an ad-hoc distribution channel for developers, distributing Apple devices as development devices. In other words, we have ios people at the company install development kits for you, and that’s how it works.


How do you say apple developers will be $99 a year, up to get your account 100 devices a year as a development equipment installation package, there would be no so-called on the appstore on corporate signature Can directly to the phone for you, so his shortcomings, is $99 a year, and also can have up to 100 development equipment for installation.


What is the process of implementing this super signature?





As shown in figure:

  1. Obtain the UDID of the device based on the configuration description file

  2. Submit new development devices

  3. Update the new profiles and return to the distribution platform

  4. Distribute the signed IPA package

  5. Users download and install


Here we go into actual combat operation:



Obtain the UDID of the device based on the configuration description file



You need to prepare a MobileconFig XML file like the one below.

You need to prepare an HTML file that triggers the download of XXX.mobileconfig

The HTML looks like this:



HTML code:

<! DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8"> <title> Test uUID </title> <styletype="text/css">        body {            margin: 0;            padding: 0;            color: # 333; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; The line - height: 1.42857; } #content { display:flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; text-align: center; } .buttons { background: #333333 none repeat scroll 0 0; border: 1px solid #777; color: #fff; cursor: pointer; font-family: "Microsoft Yahei", Arial, Tahoma, sans-serif; font-style: normal; font-weight: bold; padding: 20px; margin-left: 10px; text-decoration: none; text-transform: none; white-space: nowrap; font-size: 66px; }
      Copy the code


Mobileconfig file format:


<? xml version="1.0" encoding="UTF-8"? > <! DOCTYPE plist PUBLIC"- / / / / DTD PLIST Apple 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>http://localhost:8080/receive.php</string> <! -- <key>DeviceAttributes</key> <array> <string>UDID</string> <string>IMEI</string> <string>ICCID</string> <string>VERSION</string> <string>PRODUCT</string> </array> </dict> <key>PayloadOrganization</key> <string>test.udid.com</string> <key>PayloadDisplayName</key> <string> Test query uDID </string> <key>PayloadVersion</key> <integer< / a > 1integer> <key>PayloadUUID</key> <string>3C4DC7D2-E475-3375-489C-0BB8D737A653</string> <key>PayloadIdentifier</key> <string>dev.skyfox.profile-service</string> <key>PayloadDescription</key> <string> This file is only used to obtain the device ID</string> <key>PayloadType</key> <string>Profile Service</string> </dict></plist>Copy the code


When the user downloads the Mobileconfig file in Safari, the user is automatically redirected to the corresponding page



When the user triggers the install button in the upper right corner of the image above, Apple sends a POST request to the backend server address set in your Mobileconfig file, and we need to process the corresponding parsed XML data on the backend. You can obtain the UDID of the current user.


The PHP backend code looks like this:


Class UDID{/** * parsing XML data from Apple post * @param$dataString Data source * @return array     */    public function parseAppleData($data) {$udidData = [];        $plistBegin = '
      ;        $plistEnd = '</plist>';        $pos1 = strpos($data.$plistBegin);        $pos2 = strpos($data.$plistEnd);        $data2 = substr($data.$pos1.$pos2 - $pos1);        $xml = xml_parser_create();        xml_parse_into_struct($xml.$data2.$udidData);        xml_parser_free($xml);        return $udidData;    }    public function createQueryData($data) {$udidData = $this->parseAppleData($data);        list ($iterator.$arrayCleaned.$query) = [0, [], []; foreach ($udidData as $v) {            if ($v['level'] = = 3 &&$v['type'] = ='complete') {                $arrayCleaned[] = $v;            }            $iterator+ +; }$iterator = 0;        foreach ($arrayCleaned as $elem) {            switch ($elem['value']) {                case "CHALLENGE":                    $query['CHALLENGE'] = $arrayCleaned[$iterator+ [1]'value'];                    break;                case "DEVICE_NAME":                    $query['DEVICE_NAME'] = $arrayCleaned[$iterator+ [1]'value'];                    break;                case "PRODUCT":                    $query['DEVICE_PRODUCT'] = $arrayCleaned[$iterator+ [1]'value'];                    break;                case "UDID":                    $query['UDID'] = $arrayCleaned[$iterator+ [1]'value'];                    break;                case "VERSION":                    $query['DEVICE_VERSION'] = $arrayCleaned[$iterator+ [1]'value'];                    break;            }            $iterator+ +; }return $query;    }}$query = (new UDID)->createQueryData(file_get_contents('php://input')); File_put_contents (file_put_contents)'./test.log',json_encode($query));Copy the code


The final result can be seen in the file:


{"DEVICE_PRODUCT":"IPhone7, 2"."UDID":"18314ad381ff54b1862905e1253006e700000000"."DEVICE_VERSION":"14E8301"}Copy the code


Submit the new development device update profile




The next key is how to register new developer devices and update Provisioning Profiles in seconds after the user’s UDID is obtained. And here we need an open source tool (Spaceship) :




Open source libraries address: https://github.com/fastlane/fastlane/tree/master/spaceship


Installation script:


sudo gem install fastlaneCopy the code


Ruby example code:


require 'spaceship'if! ARGV[0] || ! ARGV[1] puts"Please check if the entered account password is complete \n"exitendif ! ARGV[2] puts"Please enter apple user's UDID \n"exitendif ! ARGV[3] puts"Please enter apple user identifier \n"    exitend# to create equipment (according to the need to operate the def create_device (name, udid) Spaceship: : Portal. Device. The create! (name: name, udid: Udid) end# write device number def write_devices puts "began to get certificate \ n p =" Spaceship: : Portal. Provisioning_profile. Development. All puts "Number began to add new equipment \ n" # get all device to join the device number If is to specify the need to manually create a data source is modified all_devices = Spaceship: : Portal. Device. All p.e ach do | profile | profile.devices = all_devices profile.update! Puts "update complete end puts" # "to get the latest certificate \ n" to refresh the above certificates, apple here will update BiaoShiHao p = Spaceship: : Portal. Provisioning_profile. Development. All Puts "execution profile File download" # File. The File name write (". / embedded. Mobileprovision ", Def download_cer puts "gets the certificate" prod_certs = Spaceship::Portal.certificate.development.all cert_content = prod_certs.first.download File. Write ("./ iOS_development. cer", cert_content) puts "certificate downloaded successfully \n"endbegin # landing Spaceship:: portal. login(ARGV[0], ARGV[1]) write_devices download_cer exit(1)rescue => exception printf(exception.message) exit(0)endCopy the code


Usage:


Ruby xxx.rb Apple account Apple account password UDID aliasCopy the code

One particular problem to be aware of when using the login method in this library is Apple’s two-factor authentication problem. My recommended solution is to start with the command box operation and get a month’s worth of authentication



If you do not use the device code for authorization, you can use the SMS verification code. For example, you need to add the authentication code under ~/. Bash_profile for MAC, and the password must start with +86.


export SPACESHIP_2FA_SMS_DEFAULT_PHONE_NUMBER=+861234567890Copy the code


Re-sign the corresponding ios package





There are many ways to re-sign ios, such as using library operations using Xcode commands using your own scripts and so on


We recommend the sign framework to solve this problem



Warehouse address: https://github.com/fastlane/fastlane/tree/master/sigh


Terminal use:


fastlane sigh resignCopy the code

This command is a step action that lets you select ipA package certificate code and so on


If you feel you need to automate, you can refer to the parameters of this command to modify the call


fastlane sigh resign ./path/app.ipa --signing_identity "iPhone Distribution: Felix Krause" -p "my.mobileprovision"Copy the code


Users download and install





When the preceding commands are complete, you can output back to the PHP script and download the newly signed package for distribution and download


header('the HTTP / 1.1 301 version Permanently'); header("Location: http://xxxxx/download.php?".$params);Copy the code


conclusion


Through the power of the open source community, we succeeded at key technical points throughout the mechanism. Thanks to the author of the Spaceship library, who exposed all the Apple apis, it was possible to split using ad-hoc.


Since ad-hoc is used for commercial distribution, the distribution platform bypassing APPLE’s review in this way is a serious violation of Section 3.3.3 of APPLE’s Developer Program License Agreement:


Without Apple's prior written approval or permission In accordance with Section 3.3.25 (In-App Purchase API), Applications may not provide, unlock, or activate additional features or features through Distribution channels other than the App Store, Custom App Distribution, or TestFlightCopy the code


But for those in the black industry, these provisions are ignored. But with technology, we need to put it to good use.


Join us and learn! I will update my articles to all major platforms and public accounts every day.

The way ahead is so long without ending, yet high and low I’ll search with my will unbending.