Обновление сертификатов электронной подписи с локального ЦС в Directum RX

13 2

Цели и задачи

После перехода с Directum 5.8 на RX потребовалось реализовать возможность выпуска УНЭП на локальном ЦС организации в таком же удобном для пользователей виде, как это было в старой версии СЭД. Имеющиеся реализации с использованием PowerShell не устраивали по ряду причин, в том числе невозможность в будущем использовать решение на импортозамещенной инфраструктуре. А также недостаточная гибкость такого решения.

Проблемы и их решение

В процессе реализации возникли некоторые особенности разработки в RX:

  1. Класс System.Security.Cryptography.X509Certificates.CertificateRequest не доступен в RX. Хотя данный класс не числится в запрещенных.
  2. Основной и рекомендуемый способ взаимодействия поменялся с толстого клиента на веб (начиная с версии 4.0), и прикладные разработчики утратили возможность взаимодействия с локальными ресурсами пользователя непосредственно из клиентского кода. Это также исключает возможность запроса сертификата в текущей клиентской сессии доменного пользователя.

Этапы реализации

На первом этапе для оценки возможности реализации нового решения был разработан прототип приложения на Net Core. Получив положительный результат в виде ответа от центра сертификации с парой ключей я принялся переносить код в RX.

На этапе переноса разработки возникли трудности с классом System.Security.Cryptography.X509Certificates.CertificateRequest. Сам класс виден в DDS, но при сборке возникает ошибка:

“Тип или имя пространства имен "CertificateRequest" не существует в пространстве имен "System.Security.Cryptography.X509Certificates””.

Проверка на различных версиях и разных серверах показала, что ошибка на уровне платформы системная. В связи с полученным результатом было принято решение искать обходные пути:

  1. Обертка класса System.Security.Cryptography.X509Certificates.CertificateRequest в собственную библиотеку.
  2. Разработка внешнего приложения.
  3. Использование библиотек CERTCLILib и CERTENROLLLib.
  4. Использование библиотеки Bouncy Castle.

Первый и второй варианты отсеяли, так как их трудно будет сопровождать в будущем. Было желание реализовать все непосредственно в RX.

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

Поэтому решили сделать разработку с использованием набора библиотек Bouncy Castle. Библиотека предлагает мощный набор криптографических инструментов, необходимых для работы с ключами и сертификатами на уровне кода.

Итак, определившись с набором библиотек, перешли на этап реализации решения в RX. Для этого необходимо:

  • Сформировать пару из закрытого и открытого ключа.
  • Выполнить CSR (Certificate Signing Request) в формате PKCS10, PKCS7 или CMC.
  • Отправить сформированный запрос в центр сертификации.
  • Получить в ответ сформированный сертификат.
  • Занести открытый ключ в «Цифровые сертификаты».
  • Передать пользователи закрытый ключ.

Формирование запроса

Для того, чтобы понять, каким должен получиться рабочий вариант запроса, был сформирован пример через стандартную оснастку «Сертификаты». Функционал Bouncy Castle позволяет распарсить CSR и заглянуть «внутрь» запроса.  Далее был разработан метод формирования запроса.

Для Active Directory Certificate Services (ADCS) в запросе на подпись сертификата (CSR) должны быть переданы определенные данные, чтобы сертификат можно было успешно выпустить:

  1. Subject (Субъект):
    • Common Name (CN): обычно это полное доменное имя (FQDN) сервера или имя пользователя.
    • Organization (O): Название вашей организации.
    • Organizational Unit (OU): Подразделение внутри вашей организации.
    • Locality (L): Город.
    • State or Province (ST): Штат или провинция.
    • Country (C): Двухбуквенный код страны по стандарту ISO 3166.
  2. Extensions (Расширения):
    • Subject Alternative Name (SAN): Альтернативные имена субъекта, такие как дополнительные DNS-имена, адреса электронной почты и IP-адреса. Это важно для сертификатов SSL/TLS, которые должны защищать несколько имен.
    • Key Usage: определяет, для каких целей может использоваться ключ (например, цифровая подпись, шифрование данных).
    • Extended Key Usage (EKU): определяет дополнительные допустимые использования ключа, такие как аутентификация сервера, аутентификация клиента, подпись кода и т.д.
  3. Public Key (Открытый ключ):
    • Открытый ключ, соответствующий приватному ключу, который будет использоваться для шифрования, цифровых подписей и других криптографических операций.
  4. Optional Attributes (Необязательные атрибуты):
    • Email Address: Адрес электронной почты субъекта.
    • UPN (User Principal Name): Обычно используется для сертификатов пользователей в Active Directory, представляет собой логин пользователя в формате email (например, user@domain.local).

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

Формирование CSR заняло немного больше времени, чем ожидалось. Документация Bouncy Castle в основном предназначена для Java и хотя перевести с него на C# особой трудности не вызывало, некоторые особенности дали о себе знать.

Далее с помощью метода RsaKeyPairGenerator класса Org.BouncyCastle.Crypto.Generators генерируем пару ключей и формируем CSR в формате PKCS10 добавив в  него сгенерированные закрытый и открытый ключи. Для этого используем экземпляр класса Org.BouncyCastle.Pkcs.Pkcs10CertificationRequest.

Запрос сертификата

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

Обработка ответа

В результате выполнения запроса к AD CA, мы получаем цепочку сертификатов в формате p7b. Остается только извлечь закрытый и открытый ключ и импортировать их в RX.

Открытый ключ необходимо добавить в справочник «Цифровые сертификаты», для этого создаем новую запись в справочнике, указываем в качестве владельца текущего пользователя, а все остальные данные заполняем значениями из сертификата.

// Добавляем в сертификаты RX.
int CertificateInfoMaxStringLength = 250;
var certificateInfo = Sungero.Core.CertificateUtils.GetCertificateInfo(certificateData);
var newCert = Sungero.CoreEntities.Certificates.Create();
newCert.Owner = Sungero.CoreEntities.Users.As(employee);

newCert.X509Certificate = certificateData;
newCert.Issuer = certificateInfo.Issuer?.Length > CertificateInfoMaxStringLength ? certificateInfo.Issuer.Substring(0, CertificateInfoMaxStringLength) : certificateInfo.Issuer;
// Срок действия сертификата из файла (NotAfter и NotBefore) возвращается в локальном времени машины, на которой исполняется данный код.
newCert.NotAfter = certificateInfo.NotAfter.FromUtcTime();
newCert.NotBefore = certificateInfo.NotBefore.FromUtcTime();
newCert.Subject = certificateInfo.Subject?.Length > CertificateInfoMaxStringLength ? certificateInfo.Subject.Substring(0, CertificateInfoMaxStringLength) : certificateInfo.Subject;
newCert.Thumbprint = certificateInfo.Thumbprint;
newCert.Description = Resources.UnepAbbreviation;

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

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

Результаты

В результате пользователи получили простой инструмент для обновления своих УНЭП в RX.

Для получения нового сертификата пользователю достаточно выполнить несколько простых действий:

1. С обложки модуля запустить установку ЭЦП, ввести пароль от своей учетной записи.


 

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


 

 

 

 

 

 

 

 

 

Денис Николаев

Как ограничивается доступ к закрытым ключам в системе (ключи которые находятся во вложениях задачи/уведомления)? Удаляются ли они потом? Как решается ограничение прав при замещениях?

Евгений Романчук

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

Авторизуйтесь, чтобы написать комментарий