Как стать автором
Обновить

Распространение iOS приложений по ссылке в корпоративной среде, используя стек технологий Microsoft

Время на прочтение23 мин
Количество просмотров12K


Недавно стало известно о покупке Xamarin компанией Microsoft. Данная новость не осталась незамеченной среди сообщества разработчиков, а также, среди корпоративных клиентов. В связи с этим, становятся более актуальны истории, где в Microsoft Full Stack окружение необходимо интегрировать мобильные решения, не приводящие при этом к необходимости драматического расширения набора компетенции IT-отела, либо компании в целом. Для таких сценариев становится лаконичным выбор Xamarin, как компонента, хорошо вписывающегося в пазл, состоящий из SharePoint, ASP.NET MVC, WebAPI services и Azure.
В рамках данной статьи описывается метод распространения мобильного приложения внутри компании, преимущественно на этапе прототипирования, используя перечисленный стек технологий Microsoft.
Описываемый в статье метод, несмотря на уклон в .NET окружение, применим к iOS-приложениям, написанным при помощи любого другого инструмента разработки, будь то Apache Cordova или классический натив.


Введение



В своей предыдущей статье я рассматривал способ распространения корпоративных iOS-приложений через MDM-решение на примере OS X Server. В этой статье речь пойдет о методе распространения приложений по ссылке, используя в арсенале стек технологий Microsoft.

Почему в статье делается акцент на инструменты Microsoft? Ответ лежит на поверхности. Если компания крупная, то, скорее всего, мы имеем дело с SharePoint, а значит, и сотрудников, имеющих опыт разработки под .NET. Однако, все тоже самое можно сделать и на других технологиях, к примеру, PHP.

Как правило, метод распространения по ссылке выбрают в случаях, когда количество пользователей очень ограничено и им не нужно автоматическое обновление приложения, либо, на время разработки прототипа, когда есть группа тестировщиков или кураторов проекта внутри компании.

Примечание
Microsoft также имеет MDM-решение, построенное на базе System Center Configuration Manager и Windows Intune. Вы можете получить о них более подробную информацию, перейдя по ссылкам раз, два и три.


Постановка задачи



Давате определимся с задачами, которые перед нами ставят, и ресурами, имеющимися у нас, для их выполнения:

  • Приложение должно распространяться просто как файл, доступный для установки;
  • Решение должно быть реализовано без использования сторонних специализированных сервисов;
  • Для создания приложения используется стандартная учетная запись разработчика Apple Developer Program;

Перечислим минусы, которые нужно учитывать перед началом использования выбранного нами метода:

  • Для уведомления пользователей о новой версии приложения, скорее всего, будет использоваться электронная почта;
  • У нас нет инструмента для автоматического обновления приложений, в случае, если будет обнаружена критическая ошибка, либо, проблема в безопасности;
  • Вы ограничены, грубо, 100 пользователями;
  • При каждом добавлении нового пользователя вам придется вручную выполнять ряд действий и пересобирать приложение;

Краткая последовательность действий при добавлении новых пользователей (iOS-устройств) выглядит следующим образом:

  • Получить UDID устройства, которое нужно добавить;
  • Добавить UDID устройства в консоль разработчика на сайте Apple;
  • Добавить устройство в профиль распространения (Provisioning Profile) нужного приложения;
  • Пересобрать приложение с измененным Provisioning Profile;
  • Выложить новую версию приложения на веб-ресурсе;

Получение UDID устройства возможно при помощи iTunes или Apple Configurator 2. Однако, естественным желанием является автоматизация этого процесса. Это возможно при помощи профилей (iOS Сonfiguration Profile), которые представляют из себя файлы с расширением *.mobileconfig, и имеющие XML-структуру.
Если кратко, то это работает следующим образом: в файле *.mobileconfig вы указываете параметры, которые хотите запросить у iOS-устройства (в нашем случае UDID), и обратный URL, куда iOS-устройство отправит ответный XML-файл с заполненными полями.

Обеспечение возможности скачивания и установки приложения по прямой ссылке также имеет ряд особенностей, которые мы рассмотрим в данной статье. Одна из них — необходимость использования HTTPS.


Концептуальная схема и планирование работ



Чтобы вы смогли прикинуть объем действий при использовании данного метода я подготовил две схемы.

Распространение приложения по ссылке с использованием учетной записи Apple Developer Program


Распространение приложения по ссылке с использованием учетной записи Apple Developer Enterprise Program



Как видите, использование стандартной учетной записи Apple Developer Program влечет за собой много накладных расходов. Тем не менее, мы рассмотрим именно этот вариант, как наиболее сложный.

Я разбил свое повествование на три секции, чтобы было удобно воспринимать информацию в нужной последовательности. Будем называть их «шагами». Итак, давайте кратко рассмотрим, что нам предстоит сделать.

Шаг 1: Получение UDID iOS-устройства пользователя:

  • Сформировать профиль *.mobileconfig, с указанием необходимых полей;
  • Выложить на веб-сервисе профиль *.mobileconfig, доступный для скачивания;
  • Пользователь: скачивает профиль, размещенный на html-странице через Safari на iPhone;
  • Пользователь: устанавливает профиль, после чего видит html-страницу «Спасибо!»;
  • На сервере сохраняются UDID пользователя, после чего администратор вручную добавляет этот UDID в аккаунте iOS-разработчика;

Шаг 2: Установка приложения через ссылку:

  • Экспортировать *.ipa -пакет приложения в XCode и подготовить картинки;
  • При экспорте пакета указать путь к нему на сервере;
  • Выложить на сервере: manifest.plist, myApp.ipa, image.57x57.png, image.512x512.jpg — например, в одной папке app-files;
  • Разрешить на сервере скачивание файлов: .jpg .png .plist .ipa;
  • На html-странице дать спец-ссылку на manifest.plist, формата itms-services://?action=download-manifest;
  • Веб-ресурс должен быть доступен через https с валидным SSL-сертификатом;
  • Пользователь переходит по спец-ссылке и устанавливает приложение;

Шаг 3: Разворачивание в корпоративной среде на базе Windows Server 2012 (содержит изменения к Шагу 2, если вам недоступны внешние площадки с валидными SSL-сертификатами):

  • Установка и настройка DNS-сервера;
  • Установка и настройка IIS;
  • Создание и установка SSL-сертификатов;
  • Настройка и сборка сайтов для распространения;


Примечание
Я намеренно привожу подробное описание всех этапов и промежуточных действий, чтобы у людей, с даже поверхностными знаниями в данной области, не возникло затруднений в настройке описываемого решения. Поэтому, прошу быть снисходительными. Статья помечена как «обучающий материал». Спасибо.


Шаг 1: Получение UDID iOS-устройства пользователя



Для получения UDID iOS-устройства мы будем использовать ASP.NET WebAPI 2. Это удобное решение, которое сразу обеспечит нам:

  • Диалог с пользователем через MVC-контроллеры;
  • Получение и обработку XML-файла от iOS-устройства через API-контроллер;
  • Хранение файлов профиля и базы данных зарегистрированных UDID;

Создание проекта WebAPI



В качестве площадки в Шаге 1 и 2 я буду использовать Microsoft Azure. По умолчанию, для сайтов *.azurewebsites.net предоставляется валидный SSL-сертификат, который понадобится нам на Шаге 2. Конечно, для корпоративного использования, вам нужно также предусмотреть, как минимум, простейшую авторизацию. Однако, это сильно выходит за рамки данной статьи.

Сформируем файл *.mobileconfig и положим его в директорию WebAPI-проекта «Downloads/corp-apps.mobileconfig»
Содержимое файла *.mobileconfig
    <?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>PayloadContent</key>
            <dict>
                <key>URL</key>
                <string>http://iphone-udid.azurewebsites.net/api/xml</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>Corp Apps</string>
            <key>PayloadDisplayName</key>
            <string>CorpApps</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>PayloadUUID</key>
            <string>B43A078F-E0E2-4F52-B1E6-C03AD7032EDF</string>
            <key>PayloadIdentifier</key>
            <string>com.CorpApps.profile-service</string>
            <key>PayloadDescription</key>
            <string>This temporary profile will be used to find and display your current device's UDID.</string>
            <key>PayloadType</key>
            <string>Profile Service</string>
        </dict>
    </plist>



В данном файле заслуживают внимания два поля:

URL — это адрес нашего API-контроллера, который будет обрабатывать XML-файл, отправленный от iOS-устройства;
В моем случае, я укажу ссылку на сайт, размещенный в Azure, где /api/xml — POST-метод WebAPI контроллера:
http://iphone-udid.azurewebsites.net/api/xml

PayloadUUID — проще всего описать это поле, как уникальный GUID, который не должен повторяться в рамках одного iOS-устройства.
Уникальный UUID (PayloadUUID) можно сгенерировать на OS X при помощи утилиты «uuidgen»:

Использование утилиты uuidgen
    os-x-server:~ zanael$ uuidgen --
    generate a universally unique identifier
    usage: uuidgen [-hdr]
            -hdr    emit result in form suitable for coping into a header
    os-x-server:~ zanael$ uuidgen
    B43A078F-E0E2-4F52-B1E6-C03AD7032EDF
    os-x-server:~ zanael$


Результат выполнения утилиты uuidgen



Создадим XML-файл, в котором будем хранить зарегистрированные UDID. Назовем его udids.xml, и положим его в директорию WebAPI-проекта «App_Data/udids.xml».
Стуктура XML-файла для хранения зарегистрированных UDID
    <?xml version="1.0" encoding="utf-8"?>
    <udids>
        <iPhone>
            <TimeAdded></TimeAdded>
            <UDID></UDID>
          </iPhone>
    </udids>



Добавим в Web.config разрешение для скачивания файлов-профилей.
Необходимые изменения в Web.config
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <system.webServer>
             <staticContent>
                <mimeMap fileExtension=".mobileconfig" mimeType="application/x-apple-aspen-config" />
            </staticContent>
        </system.webServer>
    </configuration>



Добавим модель для хранения информации о зарегистрированных UDID в папку Models.
Файл iPhoneUDID.cs
    namespace iPhoneUDID.Models
    {
        public class iPhoneUDID
        {
            public string TimeAdded { get; set; }
            public string UDID { get; set; }
        }
    }



Создадим HomeController (MVC-контроллер) в папке Controllers и добавим в него метод для отображения списка зарегистрированных UDID из XML.
Файл HomeController.cs
    using System;
    using System.Collections.Generic;
    using System.Web.Mvc;
    using System.Xml.Linq;
    
    namespace iPhoneUDID.Controllers
    {
        public class HomeController : Controller
        {
            [HttpGet]
            public ActionResult Index()
            {
                ViewBag.Title = "Home Page";
    
                XDocument databaseXML;
                try
                {
                    databaseXML = XDocument.Load(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml"));
                }
                catch (Exception exp)
                {
                    databaseXML = new XDocument(new XElement("udids"));
                }
    
                List<Models.iPhoneUDID> UDIDs = new List<Models.iPhoneUDID>();
                XElement iPhones = databaseXML.Element("udids");
                foreach (XElement e in iPhones.Elements("iPhone"))
                {
                    UDIDs.Add(new Models.iPhoneUDID { TimeAdded = e.Element("TimeAdded").Value, UDID = e.Element("UDID").Value });
                }
                ViewBag.UDIDs = UDIDs;
    
                return View();
            }
        }
    }



Добавим View (Index.cshtml) для HomeController, который будет приветствовать пользователя и просить зарегистрировать свой UDID.
При нажатии на кнопку Take UDID пользователь скачает профиль, iOS-устройство поймет, какие параметры нам нужны и отправит их по URL, который мы указали в *.mobileconfig файле в XML-формате.
Файл /Views/Home/Index.cshtml
    <div class="jumbotron">
        <h1>Get your UDID</h1>
        <p class="lead">Please, reach this page in Mobile Safari from your iPhone.</p>
        <p><a href="/downloads/corp-apps.mobileconfig" class="btn btn-primary btn-lg">Take UDID</a></p>
    </div>
    <div class="row">
        <p>
            @{
                foreach (iPhoneUDID.Models.iPhoneUDID item in ViewBag.UDIDs)
                {
                    <h5>TimeAdded: @item.TimeAdded, UDID: @item.UDID</h5>
                }
            }
        </p>
    </div>
    <div class="row">
        @Html.ActionLink("Clear UDIDs list", "ClearHistory", "Home", null, new { @class = "btn btn-primary btn-large" })
    </div>



Создадим XmlController (WebApi2-контроллер) в папке Controllers, который будет ловить XML-файл от iOS-устройства, и сохранять полученный из него UDID в локальный XML.
Так как мы используем Windows-машину, то вторая часть файла будет в другой кодировке. Это можно исправить, используя библиотеки конвертирования *.plist. Их можно найти на GitHub, но, так как нам нужен лишь UDID, мы можем взять его элементарно из заданной позиции в строке. Это позволит нам не тянуть лишние зависимости.
Файл XmlController.cs
    using System;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
    using System.Xml.Linq;
    
    namespace iPhoneUDID.Controllers
    {
        public class XmlController : ApiController
        {
            [HttpPost]
            public HttpResponseMessage PostRawXMLMessage(HttpRequestMessage request)
            {
                string plist = request.Content.ReadAsStringAsync().Result;
                int begin = plist.IndexOf("UDID") + 20;
                int end = plist.IndexOf("<", begin);
                string UDID = plist.Substring(begin, end - begin);
    
                XDocument databaseXML;
                try
                {
                    databaseXML = XDocument.Load(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml"));
                }
                catch (Exception exp)
                {
                    databaseXML = new XDocument(new XElement("udids"));
                }
    
                databaseXML.Element("udids").Add(
                    new XElement("iPhone",
                        new XElement("TimeAdded", DateTime.Now.ToLongTimeString() + " - " + DateTime.Now.ToShortDateString()),
                        new XElement("UDID", UDID)));
                databaseXML.Save(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml"));
    
                HttpResponseMessage response = request.CreateResponse(HttpStatusCode.MovedPermanently);
                response.Headers.Add("Location", "/Thanks");
                return response;
            }
        }
    }



Создадим ThanksController (MVC-контроллер) на который будет перенаправляться пользователь после получения его UDID.
Файл ThanksController.cs
    using System.Web.Mvc;
    
    namespace iPhoneUDID.Controllers
    {
        public class ThanksController : Controller
        {
            [HttpGet]
            public ActionResult Index()
            {
                ViewBag.Title = "Thanks Page";
    
                return View();
            }
        }
    }



Добавим View (Index.cshtml) для ThanksController.
Файл /Views/Thanks/Index.cshtml
    <div class="jumbotron">
        <h1>Thanks for Attending!</h1>
    </div>



Для очистки локального XML с полученными UDID, добавим в HomeController (MVC-контроллер) метод ClearHistory.
Файл HomeController.cs
    [HttpGet]
    public ActionResult ClearHistory()
    {
        ViewBag.Title = "Home Page";
    
        XDocument databaseXML = new XDocument(new XElement("udids"));
        databaseXML.Save(Server.MapPath("/App_Data/udids.xml"));
    
        ViewBag.UDIDs = new List<Models.iPhoneUDID>();
    
        return View("Index");
    }



Так выглядит общий Layout (_Layout.cshtml) — стандартный для шаблона.
Файл _Layout.cshtml
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>@ViewBag.Title</title>
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <body>
        <div class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    @Html.ActionLink("Имя приложения", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
                </div>
                <div class="navbar-collapse collapse">
                    <ul class="nav navbar-nav">
                        <li>@Html.ActionLink("Домашняя страница", "Index", "Home", new { area = "" }, null)</li>
                        <li>@Html.ActionLink("API", "Index", "Help", new { area = "" }, null)</li>
                    </ul>
                </div>
            </div>
        </div>
        <div class="container body-content">
            @RenderBody()
            <hr />
            <footer>
                <p>© @DateTime.Now.Year – приложение ASP.NET</p>
            </footer>
        </div>
    
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/bootstrap")
        @RenderSection("scripts", required: false)
    </body>
    </html>



Теперь, все что нужно пользователю, это зайти на сайт и нажать пару кнопок.
Действия пользователя




Если нажать Более подробно









После этого увидим на сайте его UDID.
Список зарегистрированных UDID



Наконец, у нас в руках есть UDID iOS-устройства, которое нам нужно зарегистрировать в нашей учетной записи разработчика.
Перейдем на сайт Apple для разработчиков в раздел Member Center.
Подробнее








Добавим UDID iOS-устройства.
Подробнее




Если вы еще не создали App ID, сделайте это.
Подробнее




Создайте профиль распространения (Provisioning Profile) для вашего приложения.
Подробнее














Теперь в любой момент вы можете добавить устройства к этому профилю распространения.






Шаг 2: Установка приложения через ссылку



После того, как мы получили UDID iOS-устройства, добавили его к учетной записи разработчика и внесли изменения в Provisioning Profile, нам необходимо предоставить пользователю веб-ресурс, на котором будут размещены файлы, необходимые для установки приложения. В качестве такого ресурса я буду использовать Microsoft Azure, который по умолчанию предоставляет для сайтов *.azurewebsites.net валидный SSL-сертификат. В качестве имени сайта я выберу:
https://corp-apps.azurewebsites.net

Примечание
Установка приложения по ссылке будет работать только в случае, если вы будете использовать HTTPS с валидным SSL-сертификатом.

В качестве проекта для создания веб-сайта можно выбрать самый простой шаблон, без каких-либо контроллеров. Все что нам будет нужно — это HTML-страница и возможность скачивания нескольких дополнительных файлов.
Подробнее



Теперь можем начать подготовку необходимых файлов. Для этого экспортируем пакет приложения через XCode.
Подробнее















Заполним информацию, необходимую для распространения приложения. Обратите внимание на расширения файлов.

  • Title:
    myApp
  • App URL:
    https://corp-apps.azurewebsites.net/app-files/myApp.ipa
  • Display Image URL:
    https://corp-apps.azurewebsites.net/app-files/image.57x57.png
  • Full Size Image URL:
    https://corp-apps.azurewebsites.net/app-files/image.512x512.jpg

Подробнее



После завершения экспорта в целевой папке вы увидите манифест manifest.plist, в котором прописаны пути к необходимым ресурсам, а также картинкам необходимого формата и размера.
Содержимое файла manifest.plist
    <?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>items</key>
            <array>
                <dict>
                    <key>assets</key>
                    <array>
                        <dict>
                            <key>kind</key>
                            <string>software-package</string>
                            <key>url</key>
                            <string>https://corp-apps.azurewebsites.net/app-files/myApp.ipa</string>
                        </dict>
                        <dict>
                            <key>kind</key>
                            <string>display-image</string>
                            <key>url</key>
                            <string>https://corp-apps.azurewebsites.net/app-files/image.57x57.png</string>
                       </dict>
                        <dict>
                            <key>kind</key>
                            <string>full-size-image</string>
                            <key>url</key>
                            <string>https://corp-apps.azurewebsites.net/app-files/image.512x512.jpg</string>
                        </dict>
                    </array>
                    <key>metadata</key>
                    <dict>
                        <key>bundle-identifier</key>
                        <string>com.habr.hellohabr001</string>
                        <key>bundle-version</key>
                        <string>0.0.1</string>
                        <key>kind</key>
                        <string>software</string>
                        <key>title</key>
                        <string>myApp</string>
                    </dict>
                </dict>
            </array>
        </dict>
    </plist>



Теперь, нам необходимо разрешить скачивание файлов указанных типов с сервера. Это можно сделать, внеся изменения в файл Web.config.
Необходимые изменения в файле Web.config
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <system.webServer>
            <staticContent>
                <mimeMap fileExtension=".jpg" mimeType="image/jpg" />
                <mimeMap fileExtension=".png" mimeType="image/png" />
                <mimeMap fileExtension=".plist" mimeType="text/plain" />
                <mimeMap fileExtension=".ipa" mimeType="application/octet-stream" />
            </staticContent>
        </system.webServer>
    </configuration>



И, наконец, добавим для пользователей html-страницу со спец-ссылкой на скачивание приложения.
Файл index.html
    <!doctype html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>Установка приложения</title>
      <link rel="stylesheet" href="dist/bootstrap.min.css">
    </head>
    <body>
      <div class="container">
        <br><br>
        <h1 class="text-center">Hello Apache Cordova</h1>
        <p class="text-center">
            <a href="itms-services://?action=download-manifest&url=https://corp-apps.azurewebsites.net/app-files/manifest.plist" class="btn btn-info" type="button">Install App</a>
        </p>
        <br><br>
      </div>
    </body>
    </html>



Отлично, все, что необходимо сделать пользователю — это нажать несколько кнопок.
Чтобы подчеркнуть универсальность метода по отношению к инструментам разработки, в примере было использовано приложение, написанное на Apache Cordova. Тоже самое справедливо и для других инструментов, таких как Xamarin.
Действия пользователя











Шаг 3: Разворачивание в корпоративной среде на базе Windows Server 2012



Итак, мы подошли к самому сложному сценарию, когда вам недоступны внешние площадки. В этом случае, мы будем использовать Windows Server 2012 и IIS 8.

Для наглядности развернем два отдельных сайта:
  • Для получения UDID iOS-устройств
    https://iphone-udid-zzzz.com
  • Для установки приложений
    https://corp-apps-zzzz.com

Примечание
Установка HTTPS для сайта iphone-udid-zzzz.com не обязательна, просто возьмем этот случай для демонстрации конфигурирования IIS.

Для доступа к сайтам по заданным доменным именам необходим настроенный DNS-сервер (в примере контроллер домена Active Directory, DNS-сервер, и сами сайты — развернуты на одной машине).

Начнем создавать зоны прямого просмотра для наших сайтов. Для этого воспользуемся диспетчером серверов.
Подробнее











Создадим зону прямого просмотра для сайта получения UDID iOS-устройства: iphone-udid-zzzz.com.
Подробнее







В академических целях приведу настройки сети, которые важно учитывать.
Подробнее
Настройки сетевого адаптера сервера (где 192.168.0.1 — IP-адрес WiFi-маршрутизатора)



Настройки DNS для WiFi-маршрутизатора. В случае с D-Link порядок DNS-серверов важен.



Настройки сетевого адаптера виртуальной машины с Windows Server 2012.




Создадим узел (А или АААА).
Подробнее


Пропишем IP-адрес нашего сервера



Убедимся, что сайт доступен через FQDN-имя. Выполним очистку DNS кэша.





Отлично, мы видим, что все работает.


Повторим эту же цепочку действий для сайта скачки приложений: corp-apps-zzzz.com.
Подробнее







Теперь можем приступить к установке и настройке IIS.
Подробнее








Выбираем Веб-сервер (IIS).



Здесь все оставляем без изменений.



В рамках данного руководства использовалась следующая конфигурация:









Будьте осторожны, выбирая «Автоматическая перезагрузка» на боевом сервере.





Установка завершена.


Создадим заглушки для наших сайтов.
Подробнее









Так как, для установки приложений на iOS-устройства по ссылке, необходима поддержка HTTPS — приступим к созданию SSL-сертификатов. К сожалению, SSL-сертификаты, генерируемые IIS, нам не подходят, поэтому воспользуемся утилитой OpenSSL.
В данном руководстве используется утилита командной строки Win32 OpenSSL, которую можно скачать по ссылке.
Подробности по установке OpenSSL
Выберите разрядность вашей операционной системы. Для данного Windows Server 2012 был выбран Win64 OpenSSL v1.0.2e (16MB Installer)



Моменты на которые следует обратить внимание при установке.






После установки OpenSSL, создадим на диске C папку OpenSSL-Certificates.

Запустим командную строку. Создадим сертификат для iphone-udid-zzzz.com.
Использование утилиты openssl для создания сертификата
    C:\Users\devin> cd c:\OpenSSL-Certificates
    
    c:\OpenSSL-Certificates> set RANDFILE=c:\OpenSSL-Certificates\.rnd
    
    c:\OpenSSL-Certificates> set OPENSSL_CONF=C:\OpenSSL-Win64\bin\openssl.cfg
    
    c:\OpenSSL-Certificates> c:\OpenSSL-Win64\bin\openssl.exe
    
    OpenSSL> genrsa -out iphone-udid-zzzz.key 2048
    
    OpenSSL> req -new -x509 -sha256 -key iphone-udid-zzzz.key -out iphone-udid-zzzz.cer -days 365 -subj /CN=iphone-udid-zzz.com
    
    OpenSSL> pkcs12 -export -out iphone-udid-zzzz.pfx -inkey iphone-udid-zzzz.key -in iphone-udid-zzzz.cer
    Enter Export Password:
    Verifying - Enter Export Password:
    
    OpenSSL> exit
    
    c:\OpenSSL-Certificates> 


Результат выполнения





Аналогично создадим сертификат для corp-apps-zzzz.com:
Использование утилиты openssl для создания сертификата
    C:\Users\devin> cd c:\OpenSSL-Certificates
    
    c:\OpenSSL-Certificates> set RANDFILE=c:\OpenSSL-Certificates\.rnd
    
    c:\OpenSSL-Certificates> set OPENSSL_CONF=C:\OpenSSL-Win64\bin\openssl.cfg
    
    c:\OpenSSL-Certificates> c:\OpenSSL-Win64\bin\openssl.exe
    
    OpenSSL> genrsa -out corp-apps-zzzz.key 2048
    
    OpenSSL> req -new -x509 -sha256 -key corp-apps-zzzz.key -out corp-apps-zzzz.cer -days 365 -subj /CN=corp-apps-zzzz.com
    
    OpenSSL> pkcs12 -export -out corp-apps-zzzz.pfx -inkey corp-apps-zzzz.key -in corp-apps-zzzz.cer
    Enter Export Password:
    Verifying - Enter Export Password:
    
    OpenSSL> exit
    
    c:\OpenSSL-Certificates> 


Результат выполнения





Следующим шагом импортируем созданные *.pfx сертификаты в IIS.
Подробнее









Теперь мы можем изменить привязки, чтобы обеспечить HTTPS-соединение.

Для сайта: iphone-udid-zzzz.com








Для сайта: corp-apps-zzzz.com









Отлично, следующим этапом нам нужно внести изменения в проект WebAPI для сайта iphone-udid-zzzz.com.

Файл Web.config, для исключения проблем с WebDAV и ошибки 405, секция system.webServer.
Необходимые изменения в файле Web.config
    <system.webServer>
        <validation validateIntegratedModeConfiguration="false" />
        <modules runAllManagedModulesForAllRequests="true">
            <remove name="WebDAVModule"/>
        </modules>
        <handlers>
            <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
            <add name="ExtensionlessUrl-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
            <remove name="OPTIONSVerbHandler" />
            <remove name="TRACEVerbHandler" />
            <remove name="WebDAV" />
        </handlers>
        <staticContent>
            <mimeMap fileExtension=".mobileconfig" mimeType="application/x-apple-aspen-config" />
        </staticContent>
    </system.webServer>


Для сертификатов также потребуется добавить изменения в Web.config.
Подробнее
    <system.webServer>
        <staticContent>
            <remove fileExtension=".mobileconfig" />
            <mimeMap fileExtension=".mobileconfig" mimeType="application/x-apple-aspen-config" />
            <remove fileExtension=".crt" />
            <mimeMap fileExtension=".crt" mimeType="application/x-x509-ca-cert" />            
        </staticContent>
    </system.webServer>


Файл /Downloads/iphone-udid-zzzz.mobileconfig — изменен URL.
Подробнее
    <key>URL</key>
    <string>https://iphone-udid-zzzz.com:9001/api/xml</string>


В папку /Downloads/ добавлен сгенерированный сертификат iphone-udid-zzzz.cer, у которого необходимо изменить расширение на *.crt. В противном случае, на iOS-устройстве он будет открываться в браузере просто как текстовый файл.
Подробнее


Файл /Views/Home/Index.cshtml — изменены URL (href), добавлена ссылка на скачивание SSL-сертификата.
Необходимые изменения в файле Index.cshtml
    <div class="jumbotron">
        <h1>Get your UDID</h1>
        <p><a href="/downloads/iphone-udid-zzzz.mobileconfig" class="btn btn-primary btn-lg">Take UDID</a></p>
    </div>
    <div class="row">
        <a href="/downloads/iphone-udid-zzzz.crt" download class="btn btn-info">Install SSL-Certificate</a>
        <p>
            @{
                foreach (iPhoneUDID.Models.iPhoneUDID item in ViewBag.UDIDs)
                {
                    <h5>TimeAdded: @item.TimeAdded, UDID: @item.UDID</h5>
                }
            }
        </p>
    </div>
    <div class="row">
        @Html.ActionLink("Clear UDIDs list", "ClearHistory", "Home", null, new { @class = "btn btn-primary btn-large" })
    </div>


Файл /Controllers/XmlController.cs — добавлена конструкция try/catch — обертка при разборе plist (UDID).
Подробнее
    using System;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
    using System.Xml.Linq;
    
    namespace iPhoneUDID.Controllers
    {
        public class XmlController : ApiController
        {
            [HttpPost]
            public HttpResponseMessage PostRawXMLMessage(HttpRequestMessage request)
            {
                string plist = request.Content.ReadAsStringAsync().Result;
    
                string UDID = "Error";
                try
                {
                    int begin = plist.IndexOf("UDID") + 20;
                    int end = plist.IndexOf("<", begin);
                    UDID = plist.Substring(begin, end - begin);
                }
                catch (Exception exc)
                {
                    //
                }
    
    
                XDocument databaseXML;
                try
                {
                    databaseXML = XDocument.Load(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml"));
                }
                catch (Exception exp)
                {
                    databaseXML = new XDocument(new XElement("udids"));
                }
    
                databaseXML.Element("udids").Add(
                    new XElement("iPhone",
                        new XElement("TimeAdded", DateTime.Now.ToLongTimeString() + " - " + DateTime.Now.ToShortDateString()),
                        new XElement("UDID", UDID)));
                databaseXML.Save(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml"));
    
                HttpResponseMessage response = request.CreateResponse(HttpStatusCode.MovedPermanently);
                response.Headers.Add("Location", "/Thanks");
                return response;
            }
        }
    }



Можем начать сборку проекта для сайта iphone-udid-zzzz.com.
Подробнее










Копируем полученные файлы в соответствующую папку на сервере.
Подробнее




Рекомендую перезагрузить сайт.



Также, необходимо разрешить изменения файла udids.xml внутри каталога на сервере.
Подробнее







С этого момента, пользователи могут регистрировать UDID своих iOS-устройств на сайте:
https://iphone-udid-zzzz.com:9001

Действия пользователя
Установка SSL-сертификата. Это опционально.






Если нажать Более подробно








Регистрация UDID iOS-устройства.




Если нажать Более подробно








Отображение UDID iOS-устройства.




В целях тестирования POST-запроса регистрации UDID, рекомендую использовать бесплатное расширение для Google Chrome — Postman.

  • URL:
    http://iphone-udid-zzzz.com/api/xml
  • Тестовый запрос:
    xxxxxxUDIDxxxxxxxxxxxxxxxx100000000000000000002<xxxxxxxxxxx

Подробнее







Начнем сборку проекта для сайта corp-apps-zzzz.com.

Файл Web.config.
Содержание файла Web.config
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <system.webServer>
            <staticContent>
            <remove fileExtension=".jpg" />
                <mimeMap fileExtension=".jpg" mimeType="image/jpg" />
            <remove fileExtension=".png" />
                <mimeMap fileExtension=".png" mimeType="image/png" />
            <remove fileExtension=".plist" />
                <mimeMap fileExtension=".plist" mimeType="text/plain" />
            <remove fileExtension=".ipa" />
                <mimeMap fileExtension=".ipa" mimeType="application/octet-stream" />
            <remove fileExtension=".crt" />
                <mimeMap fileExtension=".crt" mimeType="application/x-x509-ca-cert" />
            </staticContent>
        </system.webServer>
    </configuration>


Файл index.html — изменены URL (href), добавлена ссылка на скачивание SSL-сертификата.
Пользователю необходимо установить этот сертификат до начала установки приложения. В противном случае, он получит ошибку проверки подлинности сертификата.
Необходимые изменения в файле index.html
    <!doctype html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Распространение iOS приложений</title>
    <link rel="stylesheet" href="dist/bootstrap.min.css">
    <script src="dist/jquery-1.11.3.min.js"></script>
    <style>
        h4 {line-height: 25px;}
    </style>
    </head>
    <body>
    <div class="container">
        <br><br>
        <h1 class="text-center">Hello Apache Cordova</h1>
        <p class="text-center">
            <a href="itms-services://?action=download-manifest&url=https://corp-apps-zzzz.com:9002/app-files/manifest.plist" class="btn btn-info" type="button">Install App</a>
            <a href="/app-files/corp-apps-zzzz.crt" download class="btn btn-info">Install SSL-Certificate</a>
        </p>
        <br><br>
    </div>
    </body>
    </html>


Файл manifest.plist — изменены URL (добавлены порты).
Необходимые изменения в файле manifest.plist
    <?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>items</key>
        <array>
            <dict>
                <key>assets</key>
                <array>
                    <dict>
                        <key>kind</key>
                        <string>software-package</string>
                        <key>url</key>
                        <string>https://corp-apps-zzzz.com:9002/app-files/myApp.ipa</string>
                    </dict>
                    <dict>
                        <key>kind</key>
                        <string>display-image</string>
                        <key>url</key>
                        <string>https://corp-apps-zzzz.com:9002/app-files/image.57x57.png</string>
                    </dict>
                    <dict>
                        <key>kind</key>
                        <string>full-size-image</string>
                        <key>url</key>
                        <string>https://corp-apps-zzzz.com:9002/app-files/image.512x512.jpg</string>
                    </dict>
                </array>
                <key>metadata</key>
                <dict>
                    <key>bundle-identifier</key>
                    <string>com.habr.hellohabr001</string>
                    <key>bundle-version</key>
                    <string>0.0.1</string>
                    <key>kind</key>
                    <string>software</string>
                    <key>title</key>
                    <string>myApp</string>
                </dict>
            </dict>
        </array>
    </dict>
    </plist>


В папку /app-files/ добавлен сгенерированный сертификат corp-apps-zzzz.cer, у которого необходимо изменить расширение на *.crt. В противном случае, на iOS-устройстве он будет открываться в браузере просто как текстовый файл.
Подробнее



Теперь, для установки приложения, пользователю необходимо нажать всего несколько кнопок, перейдя на сайт:
https://corp-apps-zzzz.com:9002

Действия пользователя
Установка SSL-сертификата.







Если нажать Более подробно








Установка приложения.








Заключение



В рамках данной статьи мы рассмотрели способ распространения iOS приложений по ссылке, используя стек технологий компании Microsoft.

В целях ознакомления я выложил демо-проекты на GitHub.

  • Исходники проектов, использующих внешние площадки для размещения, можно посмотреть тут;
  • Исходники проектов, использующих в качестве площадки windows server внутри компании, можно посмотреть тут.


Надеюсь, статья покажется полезной людям, начинающим разбираться а данной теме.
Теги:
Хабы:
Всего голосов 8: ↑7 и ↓1+6
Комментарии0

Публикации

Истории

Работа

Ближайшие события