Я думаю, каждый системный администратор задавался вопросом автоматизации учёта электронных подписей в своей организации. Потому что вопросы «Какие у нас есть электронные подписи?»«На каких компьютерах они установлены?»«Когда они заканчиваются?» и т.д. возникают регулярно.

Иногда на них отвечает юридический отдел, иногда их адресуют нам, системным администраторам. А кто в вашей организации ведёт учёт электронных подписей? Расскажите об этом в комментариях, будет интересно почитать.

Демонстрация работы скрипта для инвентаризации электронных подписей
Демонстрация работы скрипта для инвентаризации электронных подписей

Начнём с постановки задачи. Здесь, лучше всего, задачу разбить на подзадачи. И для каждой подзадачи использовать какой-то универсальный инструмент. Хорошо бы уже кем-то созданный, но если такого инструмента нету, то можно и самому попрограммировать. Когда дело доходит до программирования, я стараюсь смотреть на задачу шире. И программируя, делаю инструмент универсальнее, чтобы потом его можно было применить и для других схожих задач. Как говориться «универсальное празднует победу над любым частным». И так, наши задачи:

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

  2. Нам нужно приложение (скрипт), которое будет из указанных сертификатов получать нужную нам информацию.

  3. И вероятно нам потребуется третий инструмент, который будет агрегировать эту полученную информацию в таблицу, с которой более удобно работать.

Приступим к реализации поставленных задач.

Выполнение кода на удалённых компьютерах

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

1. Logon скрипт в Group Policy Object

В групповых политиках (GPO) Active Directory есть возможность указать скрипт, который будет срабатывать при входе, выходе пользователя или при включении, выключении компьютера. В нашем случае подойдёт событие входа пользователя.

Настройка logon скрипта через групповую политику
Настройка logon скрипта через групповую политику

Плюсы:

  • Выполняется по событию, т.е. как бы параллельно на нескольких компьютерах.

  • Сразу в нужном контексте, в контексте пользователя с сертификатами.

Минусы:

  • Нельзя выполнить скрипт по желанию, нужно ждать наступления события.

  • Нужно тонко настраивать права на папку с результатами работы скрипта.

  • Если компьютер на удалёнке, то можно никогда не получить результат.

2. Инсталляционный пакет в Kaspersky Security Center

Если у вас используется Kaspersky Security Center, то вы можете в нём создать инсталляционный пакет. В него вложить свой скрипт и выполнить его на нужной выборке компьютеров.

Создание инсталляционного пакета в Kaspersky Security Center
Создание инсталляционного пакета в Kaspersky Security Center

Плюсы:

  • Выполняется параллельно на нескольких компьютерах.

  • Можно выполнить скрипт по желанию, запустив задачу вручную.

  • Удобный и приятный интерфейс.

Минусы:

  • Выполняется не в контексте пользователя (об этом чуть позже).

  • Нужно самому думать, как собирать результаты выполнения.

3. Командлет Invoke-Command в PowerShell

Можно воспользоваться командлетом Invoke-Command в PowerShell, который позволяет выполнить нужный нам код на удалённых компьютерах. В чистом виде командлет Invoke-Command не очень интересен, т.к. он не имеет каких-либо преимуществ над описанными выше вариантами. Но если с ним поработать, сделать обвязку, добавить параллельное исполнение и другие полезные опции, то получается не плохой вариант.

Получение сертификатов пользователей с удалённых компьютеров
Получение сертификатов пользователей с удалённых компьютеров

Так собственно и появился скрипт Invoke-TaskCommand.ps1, который вы можете найти в моём репозитории на GitHub. Скрипт выполняет команду -Command на списке удалённых компьютеров -ComputerName в контексте заданной учётной записи -AccountSID. При этом скрипт может доставить на удалённый компьютер вспомогательные файлы из папки -InputPath и вернуть обратно в другую папку -OutputPath файлы, как результат работы команды. Так же есть WMI фильтры -IncludeWQL и -ExcludeWQL, в которых можно написать WQL запросы. С помощью них можно гибко исключать не нужные нам компьютеры из списка и не делать лишнюю работу по доставке вспомогательных файлов на эти компьютеры. В скрипте задействован PowerShell модуль ThreadJob что распараллеливает обход компьютеров в 16 потоков. И благодаря этому пробежка по 1000 компьютеров занимает не 1,5 часа, а 15 - 20 минут. Нужная нам команда выглядит следующим образом:

$ComputerNames = Get-Content -Path '..\Data\List\Computer\All.txt' -Encoding 'Unicode';
$Command = 'powershell.exe -Command Get-ChildItem -Path CERT:\CurrentUser\My | ForEach-Object { Export-Certificate -Cert $PSItem -FilePath ($PSItem.Thumbprint + .cer);};';
.\Invoke-TaskCommand.ps1 -TaskName "Export Certificate" -Command $Command -ComputerName $ComputerNames -AccountSID "S-1-5-32-545" -OutputPath $ExportLocation;

Мы просто подключаемся к удалённому компьютеру и извлекаем все сертификаты (открытые части) электронных подписей и доставляем их в папку на наш компьютер. Отдельно стоит обсудить -AccountSID. Нюанс заключается в том, что когда мы подключаемся к удалённому компьютеру, то мы работаем в контексте своей учётной записи, из-под которой мы запускаем скрипт. Но нужные нам сертификаты находятся не в хранилище нашего пользователя, а в хранилище пользователя, который сейчас работает за компьютером. Так как же нам туда попасть, не зная логина и пароля?

Смена контекста пользователя через Планировщик заданий
Смена контекста пользователя через Планировщик заданий

Я знаю только один способ. Это на лету в скрипте создать задачу в Планировщике заданий, указать в ней, что она исполняется от имени нужного нам пользователя или группы и запустить её. И если пользователь залогинен в системе, то команда будет запущена в контексте этого пользователя. В нашем случае мы используем так называемый хорошо известный SID Пользователи. Кстати, если вы знаете ещё какие-то способы, как перейти в контекст другого пользователя, не зная его пароля, то расскажите об этом в комментариях, мне будет интересно почитать.

Так же стоит упомянуть, что список компьютеров для обхода мы не будем создавать вручную, мы просто выгрузим его из Active Directory. В данном случае, я использую другой мой скрипт env.search.min.js, про который я рассказывал в другой статье Скрипт так же опубликован на GitHub.

cscript /nologo /u env.search.min.js ldap {ABCD1234-111B-14DC-ABAC-4578F1145541} search= noalign > All.txt

Получение данных из сертификата электронной подписи

И так, мы получили файлы сертификатов, которые аккуратно сложены по папочкам с именами компьютеров. Теперь нам нужно их переименовать в удобный нам вид и получить из них данные. Для этого я написал скрипт Get-CertificateData.ps1, который вы то же можете найти в моём репозитории на GitHub.

Получение данных из сертификата с помощью PowerShell скрипта
Получение данных из сертификата с помощью PowerShell скрипта

Он принимает на вход сертификат и возвращает на выходе PSCustomObject с наиболее интересными нам атрибутами. Из интересного стоит отметить OID с идентификатором 1.2.643.2.2.49.2 его присутствие в сертификате означает наличие встроенной лицензии КриптоПРО. А чтобы переименовать файл, просто воспользуемся атрибутом CER-NAME возвращаемого объекта, он как раз создан для этой задачи.

$Certificate = Get-PfxCertificate -FilePath $File.FullName;
$CertificateName = .\Get-CertificateData.ps1 -Certificate $Certificate -DataKey "CER-NAME" -Expanded -FixExpire;
Rename-Item -Path $File.FullName -NewName ($CertificateName + $File.Extension);

Агрегируем информацию из сертификатов в таблицу

В этой задачи ничего сложного нет, мы вообще её выполним одной строкой, просто передавая объекты по конвейеру в PowerShell. Сначала получим файлы сертификатов из папок. Затем из них создадим стандартные PfxCertificate объекты. Их направим в наш скрипт Get-CertificateData.ps1 и получим PSCustomObject объекты с нашими атрибутами. Их сконвертируем в CSV строки и направим в результирующий файл.

Get-ChildItem -Path $Folder.FullName -File | Get-PfxCertificate | .\Get-CertificateData.ps1 -DataKey "NET-HOST" -DataValue $ComputerName -FixExpire | ConvertTo-Csv -Delimiter "`t" -UseQuotes "Never" | Out-File -FilePath "Report.csv" -Encoding "Unicode";

Из интересного отмечу командлет Out-GridView в PowerShell. Он позволяет достаточно удобно представлять табличные данные в графическом виде. Воспользуемся им для отображения данных.

Import-Csv -Path "Report.csv" -Encoding "Unicode" -Delimiter "`t" | Out-GridView -Wait -Title "Реестр сертификатов";

Заключение

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

Простой учёт электронных подписей с использованием PowerShell скриптов
Простой учёт электронных подписей с использованием PowerShell скриптов

Вы можете начать с малого, просто создайте несколько папок, например Архив и Действующие. Положите в них файлы сертификатов. Рядом с папками создайте ярлычок Отчёт со следующей командой:

powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File Show-Certificates.ps1 -Rename

Скачайте в эту папку скрипт Get-CertificateData.ps1 из моего репозитории на GitHub. И ещё добавьте скрипт Show-Certificates.ps1, код которого приведён ниже. И так, уже сейчас, вы сможете легко и просто строить отчёты по своим электронным подписям.

Спасибо за внимание, надеюсь данная статья будет вам полезна. Делитесь своим мнением в комментариях.

<#
  .DESCRIPTION
  Отображает отчёт по реестру сертификатов.

  .PARAMETER Rename
  Переименовать сертефикаты перед построением отчёта.

  .NOTES
  Версия: 0.1.0
  Автор: @ViPiC
#>

[CmdletBinding()]
Param (
    [switch]$Rename
);

$Items = @();
$Folders = Get-ChildItem -Directory;
foreach ($Folder in $Folders) {
    $Files = Get-ChildItem -Path $Folder.FullName -Filter "*.cer" -File -Recurse;
    foreach ($File in $Files) {
        $Certificate = Get-PfxCertificate -FilePath $File.FullName;
        $Items += .\Get-CertificateData.ps1 -Certificate $Certificate -DataKey "CER-CATEGORY" -DataValue $Folder.BaseName -FixExpire;
        if ($Rename) {
            $CertificateName = .\Get-CertificateData.ps1 -Certificate $Certificate -DataKey "CER-NAME" -Expanded -FixExpire;
            if ($File.BaseName -ne $CertificateName) { Rename-Item -Path $File.FullName -NewName ($CertificateName + $File.Extension); };
        };
    };
};
$Items | Out-GridView -Wait -Title "Реестр электронных подписей";

Комментарии (4)


  1. ASD2003ru
    31.12.2022 04:34

    А почему не получить список компов из AD с помощью PS? Зачем там какой то js?


    1. ASD2003ru
      31.12.2022 04:40

      Еще на счет прав, посмотрите это, возможно поможет

      https://stackoverflow.com/questions/42581334/elevate-without-prompt-verb-runas-start-process


      1. ViPiC Автор
        31.12.2022 12:40

        Спасибо за информацию. Но я чуть-чуть про другую задачу. Нужен не обход User Account Control (UAC), а возможность получить данные в контексте другого пользователя, не зная его пароль. Пока такую возможность я нашёл только через Планировщик заданий.


    1. ViPiC Автор
      31.12.2022 12:33

      Да, можно и с помощью PowerShell. ????

      С использованием модуля Active Directory (нужно будет его доустановить).

      (Get-ADComputer -SearchBase "DC=demo,DC=local" -Filter "*").Name | Out-File "All.txt"
      

      Или можно воспользоваться уже имеющимся объектом DirectorySearcher.

      $Searcher = New-Object System.DirectoryServices.DirectorySearcher;
      $Searcher.SearchRoot = "LDAP://DC=demo,DC=local";
      $Searcher.Filter = "(objectCategory=Computer)";
      $List = $Searcher.FindAll().GetDirectoryEntry().Name;
      Set-Content -Value $List -Path "All.txt";
      

      JScript тут больше использовался для примера, что можно одну и ту же задачу решить разными способами. Просто скрипт env.search.min.js был под рукой, я подумал «а почему бы и нет...», т.к. нужную задачу он может решить.

      А в целом, да, лучше использовать одну технологию. ????