2.1 Архитектура JC-WebClient

JC-WebClient представляет из себя приложение на основе технологии локального Web-сервера, которое работает на стороне клиента. Приложение:

  • принимает локальные HTTPS-запросы от браузера;
  • на основе этих запросов отправляет команды токену/смарт-карте, либо выполняет программные функции;
  • возвращает результат работы в браузер.

JC-WebClient поддерживает:

  • все популярные платформы: Microsoft Windows, macOS, Linux;
  • все популярные браузеры: Google Chrome, Opera, Mozilla Firefox, Яндекс.Браузер, Apple Safari, Microsoft IE, Microsoft Edge, Спутник.

Архитектура JC-WebClient приведена на рисунке ниже:













Основные компоненты JC-WebClient:

  • Локальный Web-сервер
    • обеспечивает взаимодействие между Web-страницей и токеном/смарт-картой по протоколу HTTPS;
    • обеспечивает разделение PC/SC контекстов для различных вкладок браузера.
  • Клиентский скрипт JCWebClient.js
    • предоставляет Web-страницам JavaScript API для работы с командами и методами JC-WebClient;
    • загружается на Web-страницу с локального Web-сервера;
    • при вызове из контекста страницы Web-приложения команд или методов объектов, реализованных в файле JCWebClient.js, управление передаётся на локальный Web-сервер и далее к токену.
  • Сервис мониторинга, выполненный в виде службы ОС
    • запускает локальный Web-сервер при загрузке ОС;
    • контролирует целостность локального Web-сервера;
    • перезапускает локальный Web-сервер в случае нештатных ситуаций/сбоев в ОС.

2.2 Руководство по доработке Web-страниц

2.2.1 Подключение и инициализация JC-WebClient

Для возможности работы с JC-WebClient через JavaScript API необходимо последовательно выполнить следующие действия (полный пример первых 3-х пунктов представлен в разделе Начало работы):

1.  Загрузите скрипт JCWebClient.js с локального адреса: https://localhost:24738/JCWebClient.js. На данном этапе необходимо учесть ситуацию, если на ПК пользователя отсутствует JC-WebClient, поэтому для загрузки скрипта JCWebClient.js рекомендуется следовать следующему алгоритму:

1.1. Средствами JavaScript попытайтесь асинхронно загрузить JCWebClient.js.

1.2. Если скрипт загрузился успешно, то следует перейти к пункту 2, в противном случае - перейти к следующему шагу.

1.3. Сообщите пользователю о необходимости установить JC-WebClient и предложите вариант с его установкой. Для этого параллельно основному процессу выполните следующее:

a. Отобразите пользователю информационное сообщение о необходимости установить JC-WebClient. В этом же сообщении сообщите о том, что загрузка инсталлятора JC-WebClient на ПК пользователя начнётся автоматически, но если не началась в течение, например, 5 секунд, предложите нажать на специальную ссылку для скачивания инсталлятора с Web-сервера.
b. Выполните автоматическую загрузку инсталлятора JC-WebClient на ПК пользователя с Web-сервера вашего приложения.

Примечание: Если пользователь работает на платформе macOS, то после установки потребуется перезагрузка ПК (инсталлятор JC-WebClient уведомит об этом).

Примечание: Если пользователь использует браузер Mozilla Firefox, то после установки потребуется перезапустить все его открытые экземпляры.

1.4. Ожидайте N секунд.

1.5. Средствами JavaScript повторно попытайтесь асинхронно загрузить JCWebClient.js в ожидании, что пользователь установил JC-WebClient.

1.6. Если скрипт загрузился успешно, то следует перейти к пункту 2, в противном случае - вернитесь к пункту 1.4.

Пример реализации алгоритма загрузки скрипта JCWebClient.js

// 1. Загрузка скрипта JCWebClient.js
getJCWebClient({
    onLoad: function() {
        // Скрипт JCWebClient.js загружен и модуль JCWebClient2 установлен
        console.log('JC-WebClient установлен');
    },
    onError: function(error) {
        // Скрипт JCWebClient.js не загружен
 
        this.onError = undefined; // очищаем callback для того, чтобы код ниже выполнился только самый первый раз
 
        // Отобразите пользователю информационное сообщение о необходимости установить JC-WebClient.
        // В этом же сообщении сообщите о том, что загрузка инсталлятора JC-WebClient на ПК пользователя
        // начнётся автоматически, но если не началась в течение, например, 5 секунд,
        // предложите нажать на специальную ссылку для скачивания инсталлятора с Web-сервера.
        console.log('Установите JC-WebClient !!!');
    }
});
 
/** Функция цикличной загрузки модуля JCWebClient2.
    Асинхронная функция.
 
    @param params - объект с callback-функциями:
        onLoad - функция срабатывает при успешной установке модуля JC-WebClient. Может быть вызвана только один раз;
        onError - функция срабатывает неудачной установке модуля JC-WebClient. Может быть вызвана несколько раз.
*/
function getJCWebClient(params) {
    // Проверка наличия модуля JCWebClient2 на Web-странице
    if (typeof(JCWebClient2) !== 'undefined') {
        // Модуль JCWebClient2 установлен
        params.onLoad();
        return;
    }
 
    // Получение скрипта JC-WebClient
    getScript(
        'https://localhost:24738/JCWebClient.js',
        params.onLoad,
        function(error) {
            if(typeof(params.onError) === 'function') {
                params.onError(error);
            }
 
            // Повторная попытка получения скрипта после 2-х секундного таймаута
            setTimeout(function() {
                getJCWebClient(params);
            }, 2000);
        }
    );
}
 
/** Функция загрузки скрипта.
 
    @param src - адрес расположения скрипта;
    @param done - callback-функция, срабатывающая при успешной загрузки скрипта;
    @param fail - callback-функция, срабатывающая при неудачной загрузки скрипта.
*/
function getScript(src, done, fail) {
    var parent = document.getElementsByTagName('body')[0];
 
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = src;
 
    if (script.readyState) {  // IE
        script.onreadystatechange = function () {
            if (script.readyState === "loaded" || script.readyState === "complete") {
                script.onreadystatechange = null;
                // На некоторых браузерах мы попадаем сюда и в тех случаях когда скрипт не загружен,
                // поэтому дополнительно проверяем валидность JCWebClient2
                if (typeof (JCWebClient2) === 'undefined') {
                    onFail("JCWebClient is invalid");
                }
                else {
                    done();
                }
            }
            else if (script.readyState !== "loading") {
                onFail("JCWebClient hasn't been loaded");
            }
        }
    }
    else {  // Others
        script.onload = done;
        script.onerror = function() {
            onFail("JCWebClient hasn't been loaded");
        };
    }
 
    parent.appendChild(script);
 
    function onFail(errorMsg) {
        parent.removeChild(script);
        fail(errorMsg);
    }
}


 2. После успешной установки модуля JCWebClient2 инициализируйте его функцией initialize.

// 2. Инициализация JC-WebClient
JCWebClient2.initialize();


3. Проверьте версию
 JC-WebClient.

На данном этапе следует проверить, соответствует ли версия JC-WebClient пользователя той, которая поддерживается Вашим Web-приложением. Для этого рекомендуется следовать следующему алгоритму:

3.1. Асинхронно получите версию JC-WebClient командой getJCWebClientVersion.

3.2. Если версия поддерживается Вашим Web-приложением (т.е. Вы провели тестирование этой версии с Вашим Web-приложением), то следует перейти к пункту 4, в противном случае - перейти к следующему шагу.

3.3. Сообщите пользователю о том, что для корректной работы Web-приложения необходима другая версия JC-WebClient и далее можете:

a. прервать работу с пользователем;

b. предложить установить необходимую версию JC-WebClient (аналогично пункту 1.3);

c. дать возможность пользователю самому выбрать его дальнейшие действия (a или b).

3.4. Ожидайте N секунд.

3.5. Повторно асинхронно получите версию JC-WebClient командой getJCWebClientVersion в ожидании, что пользователь установил необходимую версию.

3.6. Если версия поддерживается Вашим Web-приложением, то перезагрузите текущую Web-страницу, в противном случае - вернитесь к пункту 3.4.

Пример реализации проверки версии JC-WebClient

// 3. Цикличная проверка версии JC-WebClient
checkJCWebClientVersion(function() {
    // Версия JC-WebClient корректна
    console.log('Версия JC-WebClient корректна. Продолжаем работу...');
});
 
/* Функция цикличной проверки версии JC-WebClient.
   Асинхронная функция.
 
   @param done - callback-функция, срабатывающая при успешной проверке версии JC-WebClient.
*/
function checkJCWebClientVersion(done) {
    var failureCount = 0// Счетчик некорректных версий
 
    // Начать проверку версии JC-WebClient
    checkVersion();
 
    // Внутренняя функция проверки версии JC-WebClient
    function checkVersion() {
        // Получение версии JC-WebClient
        JCWebClient2.getJCWebClientVersion({
            async: true,
            onResult: function(version, error) {
                if(typeof(error) !== 'undefined') {
                    // Можно обработать ошибки в соответствии с бизнес-логикой
                    console.log('Ошибка получения версии JC-WebClient: ' + error);
                }
 
                // Проверка версии JC-WebClient
                if(/* Условие некорректности version */) {
                    // Версия не подходит
                    ++failureCount;
 
                    if(failureCount === 1) {
                        // Сообщите пользователю о том, что для корректной работы Web-приложения
                        // необходима версия JC-WebClient, отличная от текущей.
                        // Это сообщение отобразится только самый первый раз
                        console.log('Обновите JC-WebClient !!!');
                    }
 
                    // Повторная проверка версии JC-WebClient после 2-х секундного таймаута
                    setTimeout(function() {
                        checkVersion();
                    }, 2000);
                    return;
                }
 
                if(failureCount !== 0) {
                    // Перезагрузить страницу, т.к. ранее был подключен JC-WebClient неподходящей версии
                    window.location.reload();
                    return;
                }
 
                // Версия JC-WebClient корректна
                done();
            }
        });
    }
}

4. Рекомендуем подписаться на уведомления от JC-WebClient для отслеживания событий подключения/отключения токенов и изменения состояния аутентификации (см. Работа с событиями).

2.2.2 Базовые команды для работы с токенами

После описанных выше действий Web-страница готова к последующей работе с JC-WebClient.

При работе с USB-токенами и смарт-картами (устройствами) необходимо выбрать на устройстве криптографический токен. На одном устройстве может быть несколько токенов. Различные токены обладают различной функциональностью: например, GOST 2 реализует российские криптоалгоритмы, а PKI – западные. Для работы с токенами в JC-WebClient API предусмотрены слоты.

1. Для получения массива слотов всех токенов, подключенных к ПК, используйте команду getAllSlots:

var slots = JCWebClient2.getAllSlots();

Например, если к ПК подключено устройство JaCarta-2 PKI/ГОСТ (на нём 2 поддерживаемых токена – PKI и GOST 2), то команда getAllSlots вернёт массив из двух объектов с разными идентификаторами слотов: один – для токена PKI, второй – для токена GOST 2.

Схема ниже иллюстрирует общий случай связи слотов с приложениями и устройствами:

Из схемы видно, что при подключении нескольких устройств, каждый токен устройства будет закреплен за отдельным слотом, которые используются как идентификаторы в JC-WebClient API.

После получения массива слотов выберите токен, с которым вы хотите продолжать работать. Используйте команду getTokenInfo для получения информации о токене. Отсортировать массив токенов по их типу можно следующим образом:

var TokenGOST2 = [];
var TokenPKI = [];
 
for (i = 0; i < slots.length; i++) {
   var slot = slots[i];
 
   // Получаем информацию о токене
   try {
     var tokenInfo = JCWebClient2.getTokenInfo({
        args: {
           tokenID: slot.id
        }
     });
 
     switch(tokenInfo.type) {
          case JCWebClient2.Vars.TokenType.gost2: {
           TokenGOST2.push(slot);
           break;
        }
        case JCWebClient2.Vars.TokenType.pki: {
           TokenPKI.push(slot);
           break;
        }
     }
   }
   catch(error) {
      console.error("Error: " + error);
   }

3. Далее получите информацию о сертификатах, загруженных на токен. Для этого используйте следующие команды:

a. getContainerList – получение списка контейнеров;

b. getStandaloneCertificateList – получение списка автономных сертификатов;

c. getCertificateBody - получение сертификата с токена;

d. parseX509Certificate – парсинг сертификата.

4. Полученную о сертификатах информацию необходимо отфильтровать и

a. либо выбрать сертификат для работы с системой автоматически на основе заданных в Web-приложении фильтров и критериев;

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

5. После выбора сертификата, необходимо аутентифицироваться на токене с помощью PIN-кода пользователя. Без аутентификации токен будет работать в гостевом режиме с ограниченным функционалом: нельзя генерировать ключевые пары, создавать / изменять / удалять объекты на токене, создавать подпись и т.д. Для аутентификации необходимо запросить у пользователя PIN-код пользователя и использовать его при выполнения команды bindToken.

6. После успешной аутентификации токен переходит в режим пользователя, все пользовательские операции становятся доступными.

7. Узнать в каком режиме работает токен можно:

a. с помощью команды getLoggedInState;

b. с помощью события loginStateChangedEvent.

8. Для более подробного описания работы с командами см. раздел Выполнение команд.

9. С дополнительными примерами работы с токеном можно ознакомиться в разделе Примеры.

2.2.3 Разделение контекста между вкладками браузера

В JC-WebClient реализовано разделение контекста (сеансов работы с токеном) при работе из различных вкладок браузера. Это означает, что сеанс работы с токеном в режиме пользователя, установленный из одной вкладки, недоступен для других вкладок этого или другого браузера.

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

JC-WebClient предоставляет возможность явного указания на необходимость обнуления контекста при выходе за пределы данной Web-страницы. Для этого установите на этой Web-странице значение параметра JCWebClient2.saveSession в false:

JCWebClient2.saveSession = false;

Указанное значение параметра будет действовать только в пределах данной Web-страницы.

В JC-WebClient реализован таймаут в размере 1 минуты, по истечении которого контекст автоматически обнуляется, если приложение определило, что Web-страница перестала отвечать. При этом также удаляется и сеанс работы с токеном. Это произойдёт, например, в следующих случаях:

  • после закрытии Web-страницы (в том числе средствами DOM и jQuery) или браузера;
  • после перехода в данной вкладке на любую другую Web-страницу, которая не поддерживает работу с JC-WebClient.

Примечание: Если в текущем сеансе работы с токеном был введён PIN-код, т.е. состояние аутентификации токена выше, чем JCWebClient2.Vars.AuthState.notBinded, то после обнуления контекста состояние станет равно JCWebClient2.Vars.AuthState.notBinded т.е. PIN-код потребуется вводить заново.

2.3 Работа с готовым примером “Демобанк” 

2.3.1 Развертывание примера “Демобанк”

Предупреждение: Перед началом установки готового примера убедитесь, что сервер удовлетворяет всем программным требованиям “Демобанка” (см. Программные требования).

Если все начальные программные требования были удовлетворены, то можно приступить к установке примера. Мы будем исходить из того, что IIS уже установлен.

Примечание: Если на Вашей машине не установлен IIS, его можно установить следующим образом: Пуск  Панель Управления  Программы  Включение или отключение компонентов Windows. В появившемся окне отметить к установке Службы IIS и Службы IIS  Компоненты разработки приложений  ASP.NET и нажать OK. Обратите внимание, что .NET Framework должен быть установлен строго после установки служб IIS.

  1. Войдите в систему от имени пользователя с правами администратора.
  2. В папку C:\inetpub\wwwroot скопируйте всё содержимое папки с примером, которая называется demobank2.Следует скопировать не саму папку, а именно ее содержимое.
  3. Среди скопированных файлов и папок должна быть папка App_Data. Настройте права доступа к этой папке для группы пользователей IIS_IUSRS.

Для этого щелкните правой кнопкой мыши на название папки, в контекстном меню выберите пункт Свойства и перейдите на вкладку Безопасность. Нажмите кнопку Изменить.

В открывшемся окне Разрешения для группы проверьте, что в списке Группы или пользователи есть группа с именем IIS_IUSRS. Если нет, то нажмите кнопку Добавить, чтобы добавить новую группу пользователей.

В окне Выбор введите в поле ввода имя группы IIS_IUSRS и нажмите ОК.

  4. В окне Разрешения для группы (см. выше) выберите группу IIS_IUSRS, щелкнув на нее мышкой,а затем установите флажок Полный доступ → Разрешить. Нажмите кнопку Применить, а затем кнопку ОК. В окне Свойства также нажмите ОК, чтобы закрыть его.

  5. Запустите Диспетчер служб IIS. Для этого в меню Пуск выберите пункт Все приложения → Стандартные → Выполнитьи в открывшемся окне введите команду inetmgr и нажмите Enter.

  6. В окне Диспетчер служб IIS в разделе IIS два раза щелкните мышью на значок Ограничения ISAPI и CGI.

  7. В настройках ограничений ISAPI и CGI Web-сервера разрешите выполнение на Web-сервере ASP.NET версии 4.

  8. В древовидном меню слева выберите пункт Пулы приложений и щелкните два раза мышью на DefaultAppPoolдля изменения настроек пула приложений по умолчанию.

  9. В открывшемся окне Изменение пула приложений выберите версию среды Платформа .NET Framework, версия 4.0. Нажмите кнопку ОК.

Примечание: При работе в 64-разрядной ОС также необходимо в настройках Дополнительные параметры пула приложений включить поддержку 32-битных приложений.

  10. Перезапустите сервер IIS.

Перед тем как перейти к пробной сессии, необходимо убедиться, что JC-WebClient установлен на компьютере-клиенте.

2.3.2 Пробная сессия “Демобанка”

Если развертывание примера прошло успешно, то на этом этапе при переходе по адресу сервера “Демобанка” будет отображен сайт следующего вида:

  1. Перед началом работы подготовьте токен с установленным PIN-кодом пользователя.
  2. Проведите регистрацию нового пользователя в системе “Демобанк”. Откройте в боковой панели пункт Регистрация пользователя. В открывшемся диалоговом окне выберите устройство и введите желаемое имя пользователя, ниже введите PIN-код пользователя и нажмите кнопку Зарегистрировать.

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

  4. На открывшейся странице личного кабинета есть возможность протестировать основные возможности “Демобанка”, например, что-то подписать с помощью ЭП. В разделе Создание ЭП собраны все основные инструменты по работе с электронными подписями. В разделе Данные установите флажок Данные из формы и введите какой-нибудь текст в поле ввода. Это будет документ для вычисления ЭП. Нажмите ниже кнопку Создать подпись для генерации подписи. Нажмите кнопку Проверить подпись и, если все было сделано правильно, должно появиться сообщение Подпись верна. Попробуйте изменить содержимое текста, не генерируя новую подпись, в этом случае при проверке будет отображаться сообщение Подпись неверна.

В личном кабинете “Демобанка” содержится еще несколько примеров работы, но они находятся вне охвата этого руководства.