После перехода с Directum 5.8 на RX потребовалось реализовать возможность выпуска УНЭП на локальном ЦС организации в таком же удобном для пользователей виде, как это было в старой версии СЭД. Имеющиеся реализации с использованием PowerShell не устраивали по ряду причин, в том числе невозможность в будущем использовать решение на импортозамещенной инфраструктуре. А также недостаточная гибкость такого решения.
В процессе реализации возникли некоторые особенности разработки в RX:
На первом этапе для оценки возможности реализации нового решения был разработан прототип приложения на Net Core. Получив положительный результат в виде ответа от центра сертификации с парой ключей я принялся переносить код в RX.
На этапе переноса разработки возникли трудности с классом System.Security.Cryptography.X509Certificates.CertificateRequest. Сам класс виден в DDS, но при сборке возникает ошибка:
“Тип или имя пространства имен "CertificateRequest" не существует в пространстве имен "System.Security.Cryptography.X509Certificates””.
Проверка на различных версиях и разных серверах показала, что ошибка на уровне платформы системная. В связи с полученным результатом было принято решение искать обходные пути:
Первый и второй варианты отсеяли, так как их трудно будет сопровождать в будущем. Было желание реализовать все непосредственно в RX.
Третий вариант также исключили из-за сильного устаревания библиотек и возможной перспективы неработоспособности в будущем.
Поэтому решили сделать разработку с использованием набора библиотек Bouncy Castle. Библиотека предлагает мощный набор криптографических инструментов, необходимых для работы с ключами и сертификатами на уровне кода.
Итак, определившись с набором библиотек, перешли на этап реализации решения в RX. Для этого необходимо:
Для того, чтобы понять, каким должен получиться рабочий вариант запроса, был сформирован пример через стандартную оснастку «Сертификаты». Функционал Bouncy Castle позволяет распарсить CSR и заглянуть «внутрь» запроса. Далее был разработан метод формирования запроса.
Для Active Directory Certificate Services (ADCS) в запросе на подпись сертификата (CSR) должны быть переданы определенные данные, чтобы сертификат можно было успешно выпустить:
Так как автоматическое формирование запроса на основе контекста текущего доменного пользователя нам не доступно, формировать каждый параметр необходимо самостоятельно, прописывая данные с нужными 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. Открыть полученное уведомление и, согласно приложенной инструкции, установить закрытый ключ в свое личное хранилище.
Как ограничивается доступ к закрытым ключам в системе (ключи которые находятся во вложениях задачи/уведомления)? Удаляются ли они потом? Как решается ограничение прав при замещениях?
Денис, Добрый день! Для ограничения доступа к ключу используется строгий доступ. Ключ удаляется после выполнения задания.
Авторизуйтесь, чтобы написать комментарий