Как проверить наличие контейнера закрытого ключа на флешке средствами 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке механизмов контроля доступа и защиты критически важных операций в 1С часто возникает задача: разрешать выполнение определенных действий (проведение документов, закрытие периода, выгрузка данных) только в том случае, если к компьютеру пользователя подключен физический носитель — флешка или токен с закрытым ключом.

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

Архитектура работы 1С с закрытыми ключами и криптопровайдерами

Контейнер закрытого ключа (содержащий файлы header.key, masks.key, primary.key и др.) формируется специализированным криптопровайдером (например, КриптоПро CSP). Согласно стандартам безопасности, прямого программного доступа к структуре закрытого ключа нет ни у прикладного кода 1С, ни у сторонних утилит.

Проанализируем основные архитектурные ограничения:

  1. Делегирование криптографических операций: Объект платформы МенеджерКриптографии не работает с файловой системой контейнера. Он обращается к системным криптографическим интерфейсам операционной системы (CryptoAPI, CNG), которые передают запрос установленному криптопровайдеру.
  2. Роль сертификата открытого ключа: Платформа 1С выполняет поиск сертификата в системном хранилище («Личные» / «Personal»). В свойствах этого сертификата хранится ссылка на имя и расположение контейнера закрытого ключа (на съемном диске или токене).
  3. Безопасность закрытого ключа: Закрытый ключ никогда не покидает изолированную среду криптопровайдера (или защищенную память смарт-карты/токена). Провайдер лишь принимает данные, подписывает их с использованием закрытого ключа и возвращает готовую электронную подпись обратно в 1С.

Способ 1. Проверка носителя через тестовую электронную подпись (штатный механизм)

Наиболее надежный и платформенный способ убедиться в наличии флешки с закрытым ключом — попытаться выполнить операцию подписания произвольного блока данных с помощью объекта МенеджерКриптографии.

Разберем по шагам порядок настройки и реализации:

  1. Установите открытый сертификат (файл .cer) в хранилище «Личные» текущего пользователя операционной системы, связав его с контейнером на флешке (без копирования закрытого ключа в реестр компьютера).
  2. При попытке пользователя выполнить защищенное действие вызовите процедуру тестового подписания в блоке Попытка ... Исключение.
  3. Если флешка подключена, криптопровайдер успешно получит доступ к контейнеру и сформирует подпись. Если флешка извлечена, криптопровайдер вернет ошибку отсутствия носителя, которую перехватит 1С.

Посмотрим на пример реализации программной проверки:


&НаКлиенте
Функция ПроверитьНаличиеКлючевогоНосителя(ОтпечатокСертификата)
    
    РезультатПроверки = Ложь;
    
    // Инициализируем менеджер криптографии
    Менеджер = Новый МенеджерКриптографии();
    
    // Получаем сертификаты из хранилища
    Хранилище = Менеджер.ПолучитьХранилищеСертификатов(ТипХранилищаСертификатовКриптографии.ПерсональныеСертификаты);
    МассивСертификатов = Хранилище.НайтиПоОтпечатку(ОтпечатокСертификата);
    
    Если МассивСертификатов.Количество() = 0 Тогда
        ВызватьИсключение "Сертификат для проверки доступа не найден в персональном хранилище.";
    КонецЕсли;
    
    Сертификат = МассивСертификатов[0];
    
    // Формируем тестовые данные для проверки подписи
    ТестовыеДанные = ПолучитьДвоичныеДанныеИзСтроки("ПроверкаДоступа_" + Строка(ТекущаяДатаСеанса()));
    
    Попытка
        // Пытаемся подписать данные закрытым ключом
        Подпись = Менеджер.Подписать(ТестовыеДанные, Сертификат);
        РезультатПроверки = Истина;
    Исключение
        // Если флешка не вставлена или контейнер недоступен
        ИнформацияОбОшибке = ОписаниеОшибки();
        Сообщить("Ключевой носитель не обнаружен или доступ к нему заблокирован: " + ИнформацияОбОшибке);
        РезультатПроверки = Ложь;
    КонецПопытки;
    
    Возврат РезультатПроверки;
    
КонецФункции

Способ 2. Использование COM-объектов криптопровайдера (CAdESCOM)

Если требуется более глубокий контроль контекста выполнения и детальный анализ ошибок криптографической службы, можно использовать COM-компоненты криптопровайдера, такие как CAdESCOM.CPSigner и CAdESCOM.CadesSignedData.

Рассмотрим пример проверки доступности контейнера через CAdESCOM:


&НаКлиенте
Функция ПроверитьТокенЧерезCOM(ОтпечатокСертификата)
    
    Попытка
        ХранилищеCOM = Новый COMОбъект("CAdESCOM.Store");
        // Открываем личное хранилище текущего пользователя
        ХранилищеCOM.Open(2, "My", 0); // 2 - CADESCOM_CURRENT_USER_STORE, 0 - CAPICOM_STORE_OPEN_READ_ONLY
        
        НайденныеСертификаты = ХранилищеCOM.Certificates.Find(0, ОтпечатокСертификата); // 0 - CAPICOM_CERTIFICATE_FIND_SHA1_HASH
        
        Если НайденныеСертификаты.Count = 0 Тогда
            ХранилищеCOM.Close();
            Возврат Ложь;
        КонецЕсли;
        
        СертификатCOM = НайденныеСертификаты.Item(1);
        
        Подписант = Новый COMОбъект("CAdESCOM.CPSigner");
        Подписант.Certificate = СертификатCOM;
        
        ПодписанныеДанные = Новый COMОбъект("CAdESCOM.CadesSignedData");
        ПодписанныеДанные.Content = "ПроверочнаяСтрока";
        
        // Попытка создания отсоединенной подписи
        Подпись = ПодписанныеДанные.SignCades(Подписант, 1, Истина); // 1 - CADES_BES
        
        ХранилищеCOM.Close();
        Возврат Истина;
        
    Исключение
        // Ошибка возникает, если контейнер на флешке отсутствует
        Возврат Ложь;
    КонецПопытки;
    
КонецФункции

Способ 3. Аппаратная проверка через WMI (без использования криптографии)

В ситуациях, когда криптографическая стойкость не требуется, а нужно лишь убедиться, что в USB-порт вставлен конкретный съемный накопитель, можно прочитать аппаратный серийный номер устройства через WMI.

Посмотрим на пример функции, перечисляющей подключенные дисковые накопители:


&НаКлиенте
Функция НайтиПодключенныйUSBПоСерийномуНомеру(ИскомыйСерийныйНомер)
    
    Результат = Ложь;
    
    Попытка
        Локатор = Новый COMОбъект("WbemScripting.SWbemLocator");
        СервисWMI = Локатор.ConnectServer(".", "root\cimv2");
        
        // Запрашиваем только диски с интерфейсом USB
        КоллекцияДисков = СервисWMI.ExecQuery("SELECT DeviceID, InterfaceType, PNPDeviceID, SerialNumber FROM Win32_DiskDrive WHERE InterfaceType='USB'");
        
        Для Каждого Диск Из КоллекцияДисков Цикл
            // Проверяем серийный номер или PNPDeviceID устройства
            Если Найти(ВРег(Строка(Диск.PNPDeviceID)), ВРег(ИскомыйСерийныйНомер)) > 0 
                Или Найти(ВРег(Строка(Диск.SerialNumber)), ВРег(ИскомыйСерийныйНомер)) > 0 Тогда
                Результат = Истина;
                Прервать;
            КонецЕсли;
        КонецЦикла;
    Исключение
        Сообщить("Ошибка при обращении к WMI: " + ОписаниеОшибки());
    КонецПопытки;
    
    Возврат Результат;
    
КонецФункции

Важное ограничение: Метод WMI проверяет только физические параметры носителя и не защищает от клонирования или подмены данных. Для обеспечения юридической значимости и защиты от копирования используйте штатные криптографические методы формирования электронной подписи.

← На главную