Contents of this directory is archived and no longer updated.

В предыдущих статьях (раз и два) мы говорили о том, как получить схему БД центра сертификации и как получать определённые свойства каждой записи в БД. Но очень часто нам нужно будет ограничивать вывод БД по каким-то критериям. Например, мы можем хотеть получать сведения о запросах на сертификат, которые выданы на определённое имя (common name). А можем и хотеть получать сведения о ещё не одобренных запросах (хранящихся в папке Pending Requests). А может хотим отфильтровать вывод по дате, когда заканчивается срок действия сертификата. Вобщем, хотелок может быть очень много. Самый простой вариант фильтрации — получить все строки из БД, а потом через Where-Object отфильтровать нужные. Практически во всех случаях это будет плохим решением, потому что оно медленное, создаёт нагрузку на БД и очень ресурсоёмким.

Решение этой проблемы мы можем возложить на COM интерфейсы, которые будут самостоятельно извлекать только те данные, которые нам нужны (подпадают под определённый фильтр). Для этого у ICertView2 есть метод SetRestriction:

CCertView.SetRestriction( _
  ByVal ColumnIndex, _
  ByVal SeekOperator, _
  ByVal SortOrder, _
  ByVal pvarValue _
)

В свойстве ColumnIndex мы указываем номер колонки или название таблицы. Если мы захотим посмотреть запросы сертификатов, находящиеся в папке Pending Requests мы можем указать таблицу CV_COLUMN_QUEUE_DEFAULT, а если только отклонённые/ошибочные запросы, то таблицу CV_COLUMN_LOG_FAILED_DEFAULT. Вот значения для этих параметров:

  • CV_COLUMN_QUEUE_DEFAULT = –1
  • CV_COLUMN_LOG_DEFAULT = –2
  • CV_COLUMN_LOG_FAILED_DEFAULT = –3

Как видите, при использовании отрицательных значений мы задаём фильтр на уровне таблиц. Если хотим фильтровать на уровне столбцов (т.е. значение определённого столбца соответствует чему-то), значение этого аргумента должно быть натуральным и это значение должно быть равно номеру столбца. Как мы уже знаем, номер столбца можно получить при помощи метода GetColumnIndex.

Дальнейшие аргументы имеют смысл только если ColumnIndex является натуральным числом. SeekOperator задаёт уровень сравнения и они достаточно понятно расписаны в таблице. Единственное, что тут хочу отметить — это числовые значения операторов сравнения:

  • CVR_SEEK_NONE = 0x0 (должно быть указано, если ColumnIndex не является натуральным числом)
  • CVR_SEEK_EQ = 0x1
  • CVR_SEEK_LT = 0x2
  • CVR_SEEK_LE = 0x4
  • CVR_SEEK_GE = 0x8
  • CVR_SEEK_GT = 0x10

Далее идёт аргумент, задающий порядок сортировки:

  • CVR_SORT_NONE = 0
  • CVR_SORT_ASCEND = 1
  • CVR_SORT_DESCEND = 2

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

Возьмём пример, мы хотим получить все строки БД, у которых столбец Request Common Name равен "contoso-dc2-ca" (сертификаты выданные подчинённому CA). Для этого в ColumnIndex укажем 60 (номер столбца CommonName), в SeekOperator укажем 1, а в pvarValue укажем значение, которому должно оно соответствовать: contoso-dc2-ca.

Давайте попробуем создать такой фильтр. Сначала обязательная часть по инициализации интерфейса и определения отображаемых столбцов выходных объектов.

$CaView = New-Object -ComObject CertificateAuthority.View
$CaView.OpenConnection("dc1\contoso ca")
$properties = "RequestID","RequesterName","CommonName","NotBefore","NotAfter","SerialNumber"
$CaView.SetResultColumnCount($properties.Count)
$properties | %{$CAView.SetResultColumn($CAView.GetColumnIndex($False, $_))}

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

И сам фильтр:

# получаем номер столбца, по которому будет производиться фильтрация
$RColumn = $CAView.GetColumnIndex($False, "CommonName")
# устанавливаем сам фильтр
$CaView.SetRestriction($RColumn,1,0,"contoso-dc2-ca")

По умолчанию фильтр устанавливается на таблице CV_COLUMN_LOG_DEFAULT, которая содержит запросы сертификатов, содержащихся в папках Revoked Certificates, Issued Certificates и Failed Requests. Мы не можем в пределах одного фильтра указать конкретную таблицу. Но вы можете использовать несколько фильтров одновременно и каждый из них будет обрабатываться по очереди с применением логического оператора И. Т.е. в результате мы получим только те строки БД, которые подпадают под каждый фильтр. Если строка не соответствует какому-то фильтру, она будет отфильтрована и не будет отображена на выходе. Поэтому я вам покажу ещё один вариант полезного фильтра, который фильтрует строки БД по папкам (Revoked Certificates, Issued Certificates, Pending Requests, FailedRequests). В БД есть такой столбец под названием Disposition. Этот столбец имеет тип Long и вот какие полезные значения может принимать:

  • 9 – запрос ожидает одобрения (находится в папке Pending Requests);
  • 12 – архивированный сторонний сертификат;
  • 15 – обновление сертификата CA;
  • 16 – цепочка сертификатов текущего CA;
  • 20 – сертификат успешно выдан (находится в папке Issued Certificates);
  • 21 – сертификат отозван (находится в папке Revoked Certificates);
  • 30, 31 — запросы, хранящиеся в папке Failed Requests.

Поэтому если мы хотим получить выданные и неотозванные сертификаты на имя contoso-dc2-ca, мы создадим ещё один фильтр по столбцу Disposition (и значением равным 20):

$RColumn = $CAView.GetColumnIndex($False, "Disposition")
$CaView.SetRestriction($RColumn,1,0,20)

Если мы хотим добавить фильтр, что сертификат должен быть действующий, т.е. столбец NotAfter должен быть больше, чем текущие дата и время:

$RColumn = $CAView.GetColumnIndex($False, "NotAfter")
$CaView.SetRestriction($RColumn,0x10,0,[datetime]::Now)

Когда с фильтрами покончено, можно начинать шахматы:

$Row = $CaView.OpenView()
while ($Row.Next() -ne -1) {
    $cert = New-Object psobject
    $Column = $Row.EnumCertViewColumn()
    while ($Column.Next() -ne -1) {
        $current = $Column.GetName()
        $Cert | Add-Member -MemberType NoteProperty $($Column.GetDisplayName()) -Value $($Column.GetValue(1)) -Force
    }
    $Cert
    $Column.Reset()
}
$Row.Reset()

и что мы получили на выходе:

Issued Request ID           : 21
Requester Name              : CONTOSO\Administrator
Issued Common Name          : contoso-DC2-CA
Certificate Effective Date  : 2009.03.30. 13:56:53
Certificate Expiration Date : 2011.03.30. 14:06:53
Serial Number               : 6127fbc7000000000015

Issued Request ID           : 40
Requester Name              : CONTOSO\Administrator
Issued Common Name          : contoso-DC2-CA
Certificate Effective Date  : 2010.03.06. 10:56:53
Certificate Expiration Date : 2015.03.05. 10:56:53
Serial Number               : 158f4a3b000100000028

Issued Request ID           : 42
Requester Name              : CONTOSO\Administrator
Issued Common Name          : contoso-DC2-CA
Certificate Effective Date  : 2010.03.06. 11:10:31
Certificate Expiration Date : 2015.03.05. 11:10:31
Serial Number               : 159bc07a00010000002a

т.е. мы получили только те строки БД, которые подпали под каждый указанный фильтр. Если запустить этот же код 1 апреля, мы уже не получим строку под номером 21, потому что NotAfter (Certificate Expiration Date) не будет больше, чем текущее время. На сегодня всё.


Share this article:

Comments:

CHacker

Hi, I used your powershell code as a base line to create a reporitng script for a Windows CA DB. Thanks a lot for that. I now have the following issue and I am wondering if you can help me out. I want to add multiple template names to the filter of my CA View (setRestriction). But if I simply add another one it seems as if they are combined with a logical AND (or whatever) but I need it to be a logical OR so that I get results for either of the templates I am looking for. So what could be a solution to this? Currently I am creating a new ComObject with one template filter restriction each. But I am wondering if there isn't a better solution, e.g. reusing the same view or sth like that. Do oyu happen to have an approach to this? Thanks for your help and keep up the good work. Regards CHacker

Vadims Podāns

Sorry, but there are no ways to add additional filters by using OR conditions, so you have to run the code separately for each filter (by instantiating new COM object and elsewhere, as you do it now).

Comments are closed.