Logo    
Деловая газета CitCity.ru CITKIT.ru - все об Open Source Форумы Все публикации Учебный центр Курилка
CitForum    CITForum на CD    Подписка на новости портала Море(!) аналитической информации! :: CITFORUM.RU
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

20.02.2017

Google
WWW CITForum.ru
С Новым годом!

2004 г.

Windows и Delphi на защите секретов (часть 4)

Константин Виноградов, Комиздат

Первые три части статьи

Что такое сертификат? Какова его роль в защите информации? И, самое главное, - как можно использовать на практике механизм сертификации? Об этом и пойдет речь в нашей статье

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

Сертификаты

Представим себе, что существует организация, которой Боб полностью доверяет. Назовем ее Центром сертификации (Certification Authority). Алиса отсылает свой открытый ключ в Центр сертификации (ЦС). Там у нее запрашивают документы и устанавливают, действительно ли она та особа, за которую себя выдает. Затем ЦС генерирует электронный документ, содержащий информацию об Алисе и ее открытый ключ, и подписывает его своим закрытым ключом. Этот цифровой документ и является сертификатом. Сертификат может не только служить для аутентификации личности (как в случае с Алисой), но и подтверждать подлинность оборудования, веб-сайта, организации и т.п. Поэтому набор сведений, входящих в его состав, может изменяться и удовлетворять одному из стандартов.

Как правило, сертификат содержит следующие данные:

  • серийный номер сертификата;
  • ФИО (название) владельца сертификата;
  • открытый ключ владельца;
  • срок действия (даты начала и конца пригодности сертификата);
  • название ЦС;
  • электронная цифровая подпись ЦС.

Итак, ЦС удостоверяет личность Алисы и своей подписью заверяет связь между ней и ее ключом.

Поскольку сведения об Алисе подписаны, Боб может убедиться в их целостности. Для этого ему нужно обратиться в ЦС и проверить цифровую подпись, входящую в состав сертификата. Даже если Ева, о который мы упомянули вначале, перехватит сертификат и изменит сведения об Алисе, то ей придется подделывать подпись ЦС. Кроме того, Боб тоже может получить в ЦС "электронное удостоверение личности" - и тогда уверенностью в подлинности открытых ключей будут обладать оба корреспондента.

Остался один невыясненный вопрос: на чем держится доверие к Центру сертификации? Если Алиса и Боб - сотрудники одной и той же организации, а переписку ведут в деловых целях, то в качестве ЦС может выступить сама организация. Для этого ей потребуется обзавестись штатным специалистом по защите информации и поручить ему выполнение всех процедур, связанных с выдачей и проверкой сертификатов. Ну а на чем должно основываться доверие фирмы к своему сотруднику, думаем, объяснять не нужно.

В случае же, если Алиса и Боб - представители различных организаций, им придется обращаться в независимый центр сертификации. Деятельность таких центров, как правило, лицензируется службами безопасности страны или другими государственными органами. Например, украинские организации, работающие в сфере защиты информации, должны получить лицензию СБУ. Сертификату, выданному таким ЦС, можно доверять в той же степени, в какой вы доверяете паспорту гражданина некого государства.

Центры сертификации могут находиться в различных странах, работать с различным программным обеспечением, поддерживать разные стандарты сертификатов и отличающиеся процедуры их выдачи. Поэтому возникает необходимость наладить отношения доверия между самими ЦС. Эта система доверительных отношений может строиться по иерархическому принципу. Существует небольшое число ЦС, называющихся корневыми (Root Certification Authority). Они удостоверяют сертификаты дочерних центров, которые, в свою очередь, подтверждают подлинность других ЦС и т.д. В глобальном масштабе структура ЦС представляет собой несколько иерархий (см. рис. 1).

Поддержка сертификатов в CryptoAPI

Рассмотрим решение основных задач, возникающих при выпуске и обслуживании сертификатов на уровне библиотеки ОС Windows - CryptoAPI. Кстати говоря, для "внутреннего" употребления сертификатов (в рамках одной организации) процедуру лицензирования проходить не нужно. Никто ведь не может запретить вам обмениваться файлами, которые по секрету от СБУ вы будете считать цифровыми документами.

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

Запрос на получение сертификата

Рассмотрим процесс создания запроса более подробно. Для хранения данных запроса в CryptoAPI предназначена специальная структура - CERT_REQUEST_INFO. Она содержит ссылки на другие структуры, хранящие имя владельца сертификата, информацию об открытых ключах и, возможно, некоторые дополнительные атрибуты (рис. 2). В результате для создания запроса сертификата следует выполнить следующие шаги:

  1. Создать строку, содержащую поле Subject сертификата.
  2. Создать массив структур CERT_RDN_ATTR (в простейшем случае он будет состоять из единственного элемента) и инициализировать каждый его элемент следующими данными: строкой идентификатора объекта, типом хранимого значения, длиной строки, созданной на первом шаге, и самой этой строкой, приведенной к типу PBYTE. Аббревиатура RDN означает Relative Distinguished Name (относительные отличительные признаки). Структура RDN может хранить, например, имя пользователя, адрес, страну, наименование подразделения организации и т.п. При этом хранимые данные характеризуются идентифицирующей строкой: скажем, имя владельца сертификата должно иметь строку-идентификатор "2.5.4.3", двухбуквенный код страны - "2.5.4.6", и т.д. (подробное описание можно найти в справке по CryptoAPI). Способ представления данных строки описывается типом хранимого значения - например, CERT_RDN_PRINTABLE_STRING или CERT_RDN_ENCODED_BLOB.
  3. Создать массив структур CERT_RDN (в простейшем случае и он будет содержать лишь один элемент) и инициализировать каждый его элемент количеством элементов в соответствующем массиве CERT_RDN_ATTR, созданном на шаге 2, и ссылкой на первый элемент этого массива.
  4. Создать структуру CERT_NAME_INFO и инициализировать ее поля значениями количества элементов массива, созданного на шаге 3, и ссылкой на первый элемент этого массива.
  5. Вызвать функцию CryptEncodeObject, передав ей в качестве соответствующего параметра структуру CERT_NAME_INFO из шага 4. Результатом работы функции будет структура типа CERT_NAME_BLOB, содержащая входную информацию в закодированном виде. При обращении к функции следует указать тип кодировки; документация по CryptoAPI рекомендует использовать кодировку в виде комбинации следующих констант: PKCS_7_ASN_ENCODING or X509_ASN_ENCODING (другие типы могут не поддерживаться).
  6. Записать в поле Subject структуры CERT_REQUEST_INFO ссылку на структуру CERT_NANE_BLOB, созданную и инициализированную на шаге 5.
  7. Если в запрос сертификата должна быть включена некоторая дополнительная информация, нужно поместить ее в заранее созданный массив структур CRYPT_ATTR_BLOB, а ссылками на такие массивы инициализировать массив CRYPT_ATTRIBUTE. Ссылка на последний массив и количество элементов в нем записывается в соответствующие поля структуры CERT_REQUEST_INFO. Для ясности изложения этот этап рассматривать не будем.
  8. В структуру CERT_REQUEST_INFO необходимо вписать номер версии сертификата. CryptoAPI поддерживает сертификаты трех версий: 1 - сертификат содержит минимальный набор данных (работу именно с такими сертификатами мы и рассмотрим); 2 - сертификат содержит дополнительно уникальные идентификаторы владельца и издателя; 3 - сертификат включает дополнительную информацию об издателе (Центре сертификации), например его адрес электронной почты или лицензию на выпуск сертификатов.
  9. Вызвать функцию CryptExportPublicKeyInfo, которая вернет инициализированную структуру CERT_PUBLIC_KEY_INFO, содержащую открытые части ключей пользователя.
  10. Записать в поле SubjectPublicKeyInfo структуры CERT_REQUEST_INFO ссылку на структуру, созданную на шаге 9.
  11. Вызвать функцию CryptSignAndEncodeCertificate, передав ей в качестве аргумента структуру CERT_REQUEST_INFO. Эта функция закодирует структуру CERT_REQUEST_INFO и все данные, на которые в этой структуре имеются ссылки, подпишет эту закодированную информацию и еще раз закодирует уже подписанные данные.

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

{SubjectEdit - поле формы, в которое
	пользователь вводит текст
поля Subject сертификата; поле формы
	 ContainerEdit может содержать
имя контейнера ключей (если остается пустым,
	 используется контейнер
по умолчанию)}
procedure TCreateReqForm.OKBtnClick (Sender: TObject);
var nameAttr: CERT_RDN_ATTR;
nameString: PChar;
rdn: CERT_RDN;
nameInfo: CERT_NAME_INFO;
certReqInfo: CERT_REQUEST_INFO;
subjNameBlob: CERT_NAME_BLOB;
encNameLen: DWORD;
encName: PBYTE;
prov: HCRYPTPROV;
pubKeyInfoLen: DWORD;
pubKeyInfo: PCERT_PUBLIC_KEY_INFO;
encCertReqLen: DWORD;
params: CRYPT_OBJID_BLOB;
sigAlg: CRYPT_ALGORITHM_IDENTIFIER;
signedEncCertReq: PBYTE;
cont: PChar;
err: string;
encType: DWORD;
f: file;
begin
encType:= PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
nameString:= StrAlloc (length (SubjectEdit.text)+1);
StrPCopy (nameString, SubjectEdit.Text);
nameAttr.pszObjId:= '2.5.4.3';
nameAttr.dwValueType:= CERT_RDN_PRINTABLE_STRING;
nameAttr.Value.cbData:= length (SubjectEdit.text);
nameAttr.Value.pbData:= PBYTE (nameString);
rdn.cRDNAttr:= 1;
rdn.rgRDNAttr:= @nameAttr;
nameInfo.cRDN:= 1;
nameInfo.rgRDN:= @rdn;
{выясняем размер закодированного имени пользователя}
CryptEncodeObject (encType, X509_NAME,
@nameInfo, nil, @encNameLen) or (encNameLen < 1);
GetMem (encName, encNameLen);
{кодируем имя пользователя}
CryptEncodeObject (PKCS_7_ASN_ENCODING or
X509_ASN_ENCODING, X509_NAME, @nameInfo, encName,
@encNameLen) or (encNameLen < 1);
subjNameBlob.cbData:= encNameLen;
subjNameBlob.pbData:= encName;
certReqInfo.Subject:= subjNameBlob;
certReqInfo.cAttribute:= 0;
certReqInfo.rgAttribute:= nil;
certReqInfo.dwVersion:= CERT_REQUEST_V1;
if length (ContainerEdit.Text) = 0
then cont:= nil
else
begin
err:= ContainerEdit.Text;
cont:= StrAlloc (length (err) + 1);
StrPCopy (cont, err);
end;
CryptAcquireContext (@prov, cont, nil, 
	PROV_RSA_FULL, 0);
CryptExportPublicKeyInfo (prov, AT_SIGNATURE, 
	encType, nil, @pubKeyInfoLen);
GetMem (pubKeyInfo, pubKeyInfoLen);
CryptExportPublicKeyInfo (prov, AT_SIGNATURE, 
	encType, pubKeyInfo, @pubKeyInfoLen);
certReqInfo.SubjectPublicKeyInfo:= pubKeyInfo^;
FillChar (params, sizeof (params), 0);
sigAlg.pszObjId:= szOID_OIWSEC_sha1RSASign;
sigAlg.Parameters:= params;
CryptSignAndEncodeCertificate (prov, AT_SIGNATURE,
	 encType,
X509_CERT_REQUEST_TO_BE_SIGNED, @certReqInfo,
	 @sigAlg, nil, nil,
@encCertReqLen);
GetMem (signedEncCertReq, encCertReqLen);
CryptSignAndEncodeCertificate (prov, AT_SIGNATURE,
encType, X509_CERT_REQUEST_TO_BE_SIGNED,
@certReqInfo, @sigAlg, nil, signedEncCertReq,
	 @encCertReqLen);
if SaveDlg.Execute then
begin
AssignFile (f, SaveDlg.FileName);
rewrite (f, 1);
BlockWrite (f, signedEncCertReq^, encCertReqLen);
CloseFile (f);
end;
StrDispose (nameString);
FreeMem (encName, encNameLen);
if cont <> nil then StrDispose (cont);
FreeMem (pubKeyInfo, pubKeyInfoLen);
FreeMem (signedEncCertReq, encCertReqLen);
CryptReleaseContext (prov, 0);
end;

Итак, запрос сертификата создан, но что с ним делать? Если вам нужен сертификат, который можно использовать для организации безопасной связи с зарубежными партнерами, нужно отправить созданный запрос в центр сертификации, предоставив необходимые подтверждающие личность документы и уплатив соответствующую сумму. Если вы собираетесь обмениваться только в рамках сети предприятия, располагающей сервером Windows 2000 и выше, можно воспользоваться Microsoft Certificate Server. Если же вашей целью, например, является только отладка программ, или если группа, внутри которой вы собираетесь организовать обмен информацией, располагает лишь компьютерами под Windows 98 (и ниже), то можно подписать сертификат самостоятельно.

Подписание сертификата

При этом важно не забыть цель создания сертификата - он создавался, чтобы связь между именем пользователя и его открытым ключом удостоверялась подписью некоторой доверенной стороны. Такой "доверенный" пользователь внутри группы (будем называть его администратором) должен создать ключевую пару администратора и создать для нее сертификат, который он подписывает тем же закрытым ключом администратора. Этот сертификат называется корневым. Ключ администратора используется и для подписания сертификатов пользователей. Когда один пользователь хочет проверить подлинность сертификата другого пользователя, он проверяет подпись под ним администратора, используя для проверки тот самый корневой сертификат.

Итак, сделаем из только что созданного запроса сертификата подписанный корневой сертификат. Это можно проделать при помощи функций CryptoAPI. Сама процедура в документации даже не упоминается, однако ее можно "вывести" из описания соответствующих функций. Проследим ее на примере программы, позволяющей открыть файл с запросом сертификата, проверить подпись под ним и выпустить подписанный сертификат с заданным сроком действия. Программа управляется формой, показанной на рис. 3.

  • Вначале необходимо открыть запрос и проверить подпись под ним.
  • Проверяем, задано ли нестандартное имя контейнера, и подключаемся к криптопровайдеру.
  • Открываем файл с закодированным запросом сертификата и считываем его содержимое в память (указатель reqEncoded).
  • Декодируем запрос:
  • {encType, size, rsize: DWORD;
    buf: PBYTE;}
    encType:= PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
    GetMem (buf, 2048);
    rsize:= 2048;
    CryptDecodeObject (encType, X509_CERT,
    reqEncoded, size, 0, buf, @rsize);
  • В декодированном запросе выделяем и декодируем информацию, предназначенную для подписания - уже знакомую нам структуру CERT_REQUEST_INFO:
  • {p: pointer;
    signedContent: PCERT_SIGNED_CONTENT_INFO;
    DERBLOB: CRYPT_DER_BLOB;
    buf2: PBYTE;
    pCertReqInfo: PCERT_REQUEST_INFO;}
    p:= buf;
    signedContent:= p;
    DERBLOB:= signedContent^.toBeSigned;
    rsize:= 512;
    GetMem (buf2, rsize);
    CryptDecodeObject (encType, 
    	X509_CERT_REQUEST_TO_BE_SIGNED,
    DERBLOB.pbData, DERBLOB.cbData, 0, buf2, @rsize);
    p:= buf2;
    pCertReqInfo:= p;
  • Извлекаем из полученной информации строку с именем (названием) владельца сертификата, чтобы отобразить ее на форме:
  • {subjNameBLOB: CERT_NAME_BLOB;
    subjNameString: PChar;}
    subjNameBLOB:= pCertReqInfo^.Subject;
    rsize:= CertNameToStr (encType, @subjNameBLOB,
    	 CERT_SIMPLE_NAME_STR, subjNameString, 512);
    subjectEdit.Text:= subjNameString;
  • Проверяем подпись под запросом сертификата:
  • signCheckBox.Checked:= 
           CryptVerifyCertificateSignature(prov, encType,
           reqEncoded, size, 
           @(pCertReqInfo^.SubjectPublicKeyInfo));
  • Напоследок - заполняем поля формы "Действителен с" и "Действителен по" текущей датой и датой, отстоящей на год вперед:
  • NotBeforeEdit.Text:= DateTimeToStr (Now);
    NotAfterEdit.Text:= DateTimeToStr (Now + 365);
  • Освобождаем выделенную память и отключаемся от криптопровайдера.
  • После того, как пользователь убедился в правильности подписи под запросом, откорректировал при необходимости срок действия создаваемого сертификата, указал его серийный номер, вписал название издателя (администратора, центра сертификации) и указал в поле "Контейнер" имя контейнера ключей администратора, начинаем процесс подписания. Для этого следует создать и заполнить структуру CERT_INFO, являющуюся, как сказано в документации, "сердцем сертификата" (рис. 4 - серым цветом обозначены поля, содержащие закодированную информацию).
  • Подключаемся к контейнеру ключей администратора.
  • Заполняем поля сертификата, представленного структурой CERT_INFO:
  • {certInfo: CERT_INFO;
    serNum: int64;
    params: CRYPT_OBJID_BLOB;
    nameStr: PChar;
    nameAttr: CERT_RDN_ATTR;
    rdn: CERT_RDN;
    nameInfo: CERT_NAME_INFO;}
    certInfo.dwVersion:= CERT_V1; {версия 1}
    serNum:= strtoint64(serNumEdit.Text);
    certInfo.SerialNumber.cbData:= sizeof (serNum);
    certInfo.SerialNumber.pbData:= @serNum;
    FillChar (params, sizeof (params), 0);
    certInfo.SignatureAlgorithm.pszObjId:=
    	 szOID_OIWSEC_sha1RSASign;
    certInfo.SignatureAlgorithm.Parameters:= params;
    nameStr:= StrAlloc (length (IssuerEdit.text)+1);
    StrPCopy (nameStr, IssuerEdit.Text);
    nameAttr.pszObjId:= '2.5.4.3';
    nameAttr.dwValueType:= CERT_RDN_PRINTABLE_STRING;
    nameAttr.Value.cbData:= length (IssuerEdit.text);
    nameAttr.Value.pbData:= PBYTE (nameStr);
    rdn.cRDNAttr:= 1;
    rdn.rgRDNAttr:= @nameAttr;
    nameInfo.cRDN:= 1;
    nameInfo.rgRDN:= @rdn;
  • Кодируем строку, содержащую название издателя сертификата:
  • {encNameLen: DWORD;
    encName: PBYTE;
    issNameBLOB: CERT_NAME_BLOB;}
    CryptEncodeObject (encType, X509_NAME, 
    	@nameInfo, nil, @encNameLen);
    GetMem (encName, encNameLen);
    CryptEncodeObject (encType, X509_NAME, 
    	@nameInfo, encName, @encNameLen);
    issNameBlob.cbData:= encNameLen;
    issNameBlob.pbData:= encName;
    certInfo.Issuer:= issNameBLOB;
  • Кодируем даты начала и конца срока действия сертификата:
  • {sysTime: TSystemTime;}
    DateTimeToSystemTime (StrToDateTime 
    	(NotBeforeEdit.Text), sysTime);
    SystemTimeToFileTime (sysTime, certInfo.notBefore);
    DateTimeToSystemTime (StrToDateTime 
    	(NotAfterEdit.Text), sysTime);
    SystemTimeToFileTime (sysTime, 
    	certInfo.notAfter);
  • Поля сертификата, содержащие информацию о его владельце, заполняем на основании данных, содержащихся в декодированном запросе сертификата:
  • certInfo.Subject:= pCertReqInfo.Subject;
    certInfo.SubjectPublicKeyInfo:= 
    	pCertReqInfo.SubjectPublicKeyInfo;
  • В неиспользуемые поля вписываем нули и пустые указатели:
  • certInfo.IssuerUniqueId.cbData:= 0;
    certInfo.IssuerUniqueId.pbData:= nil;
    certInfo.IssuerUniqueId.cUnusedBits:= 0;
    certInfo.SubjectUniqueId.cbData:= 0;
    certInfo.SubjectUniqueId.pbData:= nil;
    certInfo.SubjectUniqueId.cUnusedBits:= 0;
    certInfo.cExtension:= 0;
    certInfo.rgExtension:= nil;
  • Подписываем и кодируем сертификат:
  • {encCertLen: DWORD;
    encCert: PByte;}
    CryptSignAndEncodeCertificate (prov, AT_SIGNATURE,
    	 encType,
    X509_CERT_TO_BE_SIGNED, @certInfo,
    	@(certInfo.SignatureAlgorithm), 
    	nil, nil, @encCertLen);
    GetMem (encCert, encCertLen);
    CryptSignAndEncodeCertificate (prov, AT_SIGNATURE,
    	 encType,
    X509_CERT_TO_BE_SIGNED, @certInfo, 
    	@(certInfo.SignatureAlgorithm), nil, 
    	encCert, @encCertLen);
  • Сохраняем подписанный и закодированный сертификат в файле, освобождаем память и дескриптор криптопровайдера.
  • Хранение сертификатов

    Созданный сертификат владелец может отправлять своим корреспондентам для организации безопасной связи. Чтобы сделать полученный сертификат доступным системе, его нужно поместить в одно из хранилищ сертификатов. Windows предусматривает существование нескольких системных хранилищ сертификатов: MY (для хранения сертификатов отдельного пользователя), СА (от Certification Authority - для хранения сертификатов центров сертификации) и ROOT (для хранения корневых сертификатов). Открытое (загруженное в память) хранилище сертификатов представляет собой связанный список блоков данных, каждый из которых содержит ссылку на следующий блок и на данные сертификата. Каждое хранилище сертификатов физически размещается либо в отдельном файле, либо в реестре Windows. При этом для работы с системными хранилищами не нужно знать их местоположения - достаточно указать приведенные выше имена.

    Для помещения сохраненного в файле подписанного сертификата в системное хранилище нужно выполнить следующие шаги.

  • Открыть файл и считать его содержимое в буфер.
  • При помощи специальной функции создать контекст сертификата:
  • {encCertLen: DWORD; - размер закодированного сертификата
    encCert: PByte; - данные сертификата
    context: PCCERT_CONTEXT;
    encType: DWORD;}
    encType:= PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
    context:= CertCreateCertificateContext
    	 (encType, encCert, encCertLen);
  • Открыть системное хранилище сертификатов:
  • {store: HCERTSTORE;}
    store:= CertOpenSystemStore (0, 'MY');
  • Добавить контекст сертификата в открытое хранилище:
  • n:= nil;
    CertAddCertificateContextToStore (store, context,
    	 CERT_STORE_ADD_REPLACE_EXISTING, n)

    Функции CertAddCertificateContextToStore, кроме дескрипторов хранилища и добавляемого контекста сертификата, передается параметр, определяющий действия системы в том случае, если в данном хранилище уже имеется идентичный сертификат. Использованная константа CERT_STORE_ADD_REPLACE_EXISTING предписывает в таком случае удалить старый сертификат и заменить его новым. Последний параметр функции позволяет получить указатель на указатель на копию сертификата, созданную при добавлении контекста в хранилище (если параметр равен пустому указателю, то ссылка не возвращается).

  • Закрыть хранилище, освободить контекст сертификата и память.
  • CertCloseStore (store, 0);
    CertFreeCertificateContext (context);
    FreeMem (encCert, encCertLen);

    Просмотреть имеющиеся в хранилище сертификаты можно с помощью функции CertEnumCertificatesInStore. Ей нужно передать дескриптор нужного хранилища и, при первом вызове, пустой указатель, а при последующих - указатель на предыдущий сертификат. Например, для просмотра содержимого одного из системных хранилищ сертификатов может быть использован следующий фрагмент программы (форма, из которой он вызывается, с результатами работы показана на рис. 5):

    {store: HCERTSTORE;
    cont, stor: PChar;
    err: string;
    cert: PCCERT_CONTEXT;
    nameString: PChar;
    size: DWORD;
    nameBLOB: CERT_NAME_BLOB;
    подключение к криптопровайдеру считается выполненным!}
    err:= CertStoreBox.Text;
    RepMemo.Lines.Add ('');
    RepMemo.Lines.Add ('====================');
    RepMemo.Lines.Add ('Contents of store ' + err);
    RepMemo.Lines.Add ('====================');
    stor:= StrAlloc (length (err) + 1);
    StrPCopy (stor, err);
    store:= CertOpenSystemStore (prov, stor);
    cert:= CertEnumCertificatesInStore (store, nil);
    nameString:= StrAlloc (512);
    while cert <> nil do
    begin
    RepMemo.Lines.Add ('---------------');
    RepMemo.Lines.Add ('Subject:');
    nameBLOB:= cert^.pCertInfo^.Subject;
    size:= CertNameToStr (encType, @nameBlob,
    	 CERT_SIMPLE_NAME_STR, nameString, 512);
    if size > 1 then
    RepMemo.Lines.Add (nameString)
    else
    RepMemo.Lines.Add ('Error');
    RepMemo.Lines.Add ('Issuer:');
    nameBLOB:= cert^.pCertInfo^.Issuer;
    size:= CertNameToStr (encType, @nameBlob,
    	 CERT_SIMPLE_NAME_STR,
    nameString, 512);
    if size > 1 then
    RepMemo.Lines.Add (nameString)
    else
    RepMemo.Lines.Add ('Error');
    cert:= CertEnumCertificatesInStore (store, cert);
    end;
    StrDispose (nameString);

    Проверка сертификата

    Получив новый сертификат, конечно, следует убедиться в корректности подписи под ним. При этом сам сертификат, скорее всего, будет помещен в личное хранилище пользователя (MY), а сертификат его издателя может находиться в хранилищах CA или ROOT. Кстати, при установке Windows в эти хранилища помещается множество сертификатов признанных центров сертификации, список которых можно просмотреть при помощи приведенной выше программы.

    Для выполнения проверки следует считать из проверяемого сертификата строку с именем его издателя и найти в одном из системных хранилищ его сертификат. Для облегчения операции поиска в нескольких хранилищах CryptoAPI 2.0 поддерживает механизм коллекций (или логических хранилищ) сертификатов. Коллекция может объединять данные из нескольких физических хранилищ и выполнять операции над всеми их сертификатами одновременно. Кстати говоря, в Windows 2000 и выше системные хранилища сертификатов сами представляют собой коллекции.

    Поиск сертификата издателя и проверка заданного сертификата выполняются одновременно функцией CertGetIssuerCertificateFromStore. Ей следует передать дескриптор хранилища сертификатов, ссылку на контекст проверяемого сертификата, возможно - ссылку на предыдущий найденный сертификат издателя (если вызов функции производится повторно) и ссылку на флаговую переменную. Эта переменная при вызове функции задает режим проверки, а при возврате - служит индикатором успеха проверки. Функция поддерживает несколько режимов проверки, которые могут комбинироваться; мы воспользуемся сочетанием двух из них - CERT_STORE_SIGNATURE_FLAG (проверить подпись издателя под заданным сертификатом) и CERT_STORE_TIME_VALIDITY_FLAG (проверить срок действия заданного сертификата).

    Рассмотрим процедуру проверки сертификата с заданным полем Subject на примере программы.

  • Подключаемся к криптопровайдеру.
  • Открываем выбранное хранилище сертификатов.
  • Считываем из формы поле Subject и ищем соответствующий сертификат в хранилище:
  • {subj: PWideChar;
    err: string;
    cert: PCCERT_CONTEXT;
    тип и значение encType - см. выше}
    err:= SubjectEdit.Text;
    GetMem (subj, 2 * length (err) + 1);
    StringToWideChar (err, subj, 2 * length (err) + 1);
    cert:= CertFindCertificateInStore (store, encType, 0,
              CERT_FIND_SUBJECT_STR, subj, nil);
    FreeMem (subj, 2 * length (err) + 1);
    if cert = nil then
    begin
    MessageDlg ('Certificate not found', 
                                  mtError, [mbOK], 0);
    CertCloseStore (store, 0);
    exit;
    end;
  • Создаем коллекцию (логическое хранилище) сертификатов:
  • {collect: HCERTSTORE;}
    collect:= CertOpenStore (CERT_STORE_PROV_COLLECTION,
                 0, 0, 0, nil);
  • Открываем и помещаем в коллекцию интересующие нас системные хранилища сертификатов - MY, CA, ROOT:
  • {mystore, castore, rootstore: HCERTSTORE;}
    mystore:= CertOpenSystemStore (prov, 'MY');
    CertAddStoreToCollection (collect, mystore, 0, 0);
    castore:= CertOpenSystemStore (prov, 'CA');
    CertAddStoreToCollection (collect, castore, 0, 2);
    rootstore:= CertOpenSystemStore (prov, 'ROOT');
    CertAddStoreToCollection (collect, rootstore, 0, 1)

    Последним параметром функции CertAddStoreToCollection задается приоритет добавляемого хранилища в коллекции. Мы ожидаем, что сертификат издателя окажется в хранилище СА, поэтому для этого хранилища задан самый высокий приоритет, для ROOT - более низкий, а для MY - низший.

  • Устанавливаем режим проверки:
  • {flags: DWORD;}
    flags:= CERT_STORE_SIGNATURE_FLAG or
    		 CERT_STORE_TIME_VALIDITY_FLAG;
  • Выполняем поиск сертификата издателя и проверку заданного сертификата:
  • {icert: PCCERT_CONTEXT;}
    icert:= CertGetIssuerCertificateFromStore 
                 (collect, cert, nil, @flags);
    if icert = nil then
    case int64(GetLastError) of
    CRYPT_E_NOT_FOUND: MessageDlg ('Issuer certificate not
    		found',  mtError, [mbOK], 0);
    CRYPT_E_SELF_SIGNED: MessageDlg ('This is root 
          certificate',  mtWarning, [mbOK], 0);
    else MessageDlg ('Invalid arguments', 
                     mtError, [mbOK], 0);
    end
    else if flags = 0
    then MessageDlg ('Certificate is valid',
                        mtInformation, [mbOK], 0)
    else MessageDlg ('Certificate is invalid', 
                     mtError, [mbOK], 0);

    В результате при правильном обращении к функции может возникнуть одна из четырех ситуаций:

    • сертификат издателя не найден - в этом случае нужно узнать издателя данного сертификата (по строке, выдаваемой, например, рассмотренной выше программой просмотра содержимого хранилища) и на его сайте взять сертификат для проверки;
    • проверяемый сертификат является корневым - использование таких сертификатов (если, конечно, это не ваш собственный сертификат, созданный под впечатлением этой статьи) требует предельного внимания, так как в этом случае подлинность данных сертификата ничем не подтверждается. Принимая такой сертификат, вы должны любыми путями убедиться, что он поступил из надежного источника (например, был помещен в хранилище ROOT при установке Windows). В противном случае вы рискуете подвергнуться такой, например, атаке: злоумышленник может прислать вам по электронной почте сообщение об обновлении корневого сертификата, заменит своими данными хранящийся у вас сертификат и сможет присылать вам фальшивые сертификаты пользователей, якобы заверенные подписью корневого центра сертификации;
    • сертификат верен - подпись издателя под проверяемым сертификатом верна, срок действия проверяемого сертификата включает текущую системную дату;
    • сертификат не прошел проверку - проверяемый сертификат либо просрочен, либо является подделкой. Правда, возможен еще один вариант: издатель имеет несколько сертификатов - и проверяемый сертификат подписан с использованием ключа из другого сертификата данного издателя. В таком случае нужно вызвать функцию CertGetIssuerCertificateFromStore повторно, передав в качестве третьего параметра контекст текущего сертификата издателя.

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

    Приложение. Архив (rar, 84,3 KB)

    Авторы ищут издателя для публикации учебного пособия по данной теме.
    Персональный сайт авторов - http://www.is.svitonline.com/vilys/.

     

    Размещение рекламы — тел. +7 495 4119920, ICQ 232284597

    Подписка на новости IT-портала CITForum.ru
    (библиотека, CITKIT.ru, CitCity)

    Новые публикации:

    24 декабря

    CITKIT.ru:

  • Новогодние поздравления
  • Сергей Кузнецов. Цикл Операционные системы: Ностальгия по будущему:

  • Алексей Федорчук. OpenSolaris 2008.11 Release

  • Сергей Голубев:

  • Евгений Чайкин aka StraNNik (Блогометки):

    17 декабря

  • С.Д.Кузнецов. Базы данных. Вводный курс

    10 декабря

    CITKIT.ru:

  • OpenSolaris 2008.11 Release

  • Альтернативные ОС: две грустные истории (С.Кузнецов)
  • Nokia N810 — доведение до ума
  • CitCity:

  • Платформа 2009: заоблачные перспективы Microsoft

    4 декабря

  • Лекция С.Д.Кузнецова Понятие модели данных. Обзор разновидностей моделей данных

    CITKIT.ru:

  • OpenSolaris 2008.11 Release. Первые впечатления

  • Linux vs FreeBSD: продолжим "Священные войны"?

  • Nokia N810 as is

  • Индульгенция для FOSS

  • Друзья СПО'2008

    26 ноября

  • Нечеткое сравнение коллекций: семантический и алгоритмический аспекты

    CitCity:

    CITKIT.ru:

  • Глава из книги А.Федорчука
    Сага о FreeBSD:
  • 19 ноября

  • Проблемы экономики производства крупных программных продуктов

  • Язык модификации данных формата XML функциональными методами

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

    Заметки к книге:

  • FreeBSD: монтирование сменных устройств и механизм HAL
  • Текстовый редактор ee

    12 ноября

  • Правило пяти минут двадцать лет спустя, и как флэш-память изменяет правила (Гоц Грейф, перевод: Сергей Кузнецов)

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:
  • OSS в России: взгляд правоведа (В.Житомирский)

  • Новая статья из цикла С.Голубева "Железный марш":

    29 октября

  • О некоторых задачах обратной инженерии

  • Веб-сервисы и Ruby

  • Тестирование web-приложений с помощью Ruby

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

  • PuppyRus Linux - беседа с разработчиком (С.Голубев)

  • Сергей Кузнецов. Заметка не про Linux

    22 октября

  • Обзор методов описания встраиваемой аппаратуры и построения инструментария кросс-разработки

    CITKIT.ru:

  • Сергей Кузнецов. Почему я равнодушен к Linux

  • Глава из книги А.Федорчука
    Сага о FreeBSD:
  • Что надо иметь
    3. Базовые познания

    CitCity:

  • Управление IT-инфраструктурой на основе продуктов Microsoft

    15 октября

  • Методы бикластеризации для анализа интернет-данных

    CitCity:

  • Разъемы на ноутбуках: что они дают и зачем их так много?
  • AMD Puma и Intel Centrino 2: кто лучше?

    CITKIT.ru:

  • Новый цикл статей С.Голубева
    Железный марш:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

    8 октября

  • Автоматизация тестирования web-приложений, основанных на скриптовых языках
  • Опыт применения технологии Azov для тестирования библиотеки Qt3

    Обзоры журнала Computer:

  • SOA с гарантией качества
  • Пикоджоуль ватт бережет
  • ICT и всемирное развитие

    CitCity:

  • Пиррова победа корпорации Microsoft

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

    Статья из архива:

  • Я живу в FreeBSD (Вадим Колонцов)

    Новые Блогометки:

  • Перекройка шаблона Blogger или N шагов к настоящему
  • Blogger. Comment style
  • Screenie или глянцевый снимок экрана

    2 октября

    CITKIT.ru:

  • Сага о FreeBSD (А. Федорчук)

    Zenwalk: пакет недели

  • Банинг — интеллектуальное развлечение (С.Голубев)

    CitCity:

    25 сентября

  • Клермонтский отчет об исследованиях в области баз данных

    CITKIT.ru:

  • Пользователям просьба не беспокоиться... (В.Попов)

  • Снова про ZFS: диск хорошо, а два лучше
  • Командная оболочка tcsh (А.Федорчук)

    Zenwalk: пакет недели

    17 сентября

  • T2C: технология автоматизированной разработки тестов базовой функциональности программных интерфейсов
  • Технология Azov автоматизации массового создания тестов работоспособности

    CITKIT.ru:

  • FreeBSD: ZFS vs UFS, и обе-две — против всех (А.Федорчук)

    Zenwalk: пакет недели

  • Дачнет — практика без теории (С.Голубев)

    10 сентября

  • За чем следить и чем управлять при работе приложений с Oracle
  • Планировщик заданий в Oracle
    (В.Пржиялковский)

    CITKIT.ru:

  • Microsoft: ответный "боян" (С.Голубев)

  • Причуды симбиоза, или снова "сделай сам" (В.Попов)

  • Файловые системы современного Linux'а: последнее тестирование
  • Zsh. Введение и обзор возможностей
    (А.Федорчук)

    Описания пакетов Zenwalk: Zsh, Thunar, Thunar-bulk-rename, Xfce4-places-plugin, Xfce4-fsguard-plugin

    Блогометки:

  • Google Chrome
  • Лончер для ASUS Eee PC 701

    3 сентября

    CITKIT.ru:

  • Заметки о ядре (А.Федорчук):

    Добавлены описания пакетов Zenwalk: Galculator, Screenshot, Gnumeric, Pidgin

    В дискуссинном клубе:

  • И еще о Википедии и Google Knol

  • Лекция для начинающего линуксоида (С.Голубев)

    26 августа

  • Транзакционная память (Пересказ: С. Кузнецов)

    CITKIT.ru:

  • Открыт новый проект Zenwalk: пакет недели

  • Статья Текстовые процессоры и их быстродействие: конец еще одной легенды?

    21 августа

    CITKIT.ru:

  • Почему школам следует использовать только свободные программы (Ричард Столлман)
  • Беседа Сергея Голубева с учителем В.В.Михайловым

  • Википедия или Гуглезнание? Приглашение к обсуждению (Алексей Федорчук)
  • Народная энциклопедия от Google (StraNNik)

  • Обзор Mandriva 2009.0 Beta 1 Thornicrofti
  • Новичок в Линукс: Оптимизируем Mandriva 2008.1

  • Книга Zenwalk. Приобщение к Linux:

    13 августа

    CitCity:

  • Мирный Atom на службе человеку. Обзор платы Intel D945GCLF с интегрированным процессором
  • Обзор процессоров Intel Atom 230 на ядре Diamondville

  • iPhone - год спустя. Скоро и в России?

    CITKIT.ru:

  • Интермедия 3.4. GRUB: установка и настройка (из книги Zenwalk. Приобщение к Linux)

    6 августа

  • СУБД с хранением данных по столбцами и по строкам: насколько они отличаются в действительности? (Пересказ: С. Кузнецов)

    CITKIT.ru:

  • Интермедия 2.2. Что неплохо знать для начала (из книги Zenwalk. Приобщение к Linux)

  • И снова про шрифты в Иксах (А.Федорчук)

  • 20 самых быстрых и простых оконных менеджеров для Linux

  • Дело о трех миллиардах (С.Голубев)

    30 июля

  • OLTP в Зазеркалье (Пересказ: С. Кузнецов)

    CitCity:

  • Будущее BI в облаках?
  • Тиражные приложения и заказная разработка. Преимущества для заказчика
  • Дискуссия со сторонниками заказной разработки

    CITKIT.ru:

  • Новые главы книги Zenwalk. Приобщение к Linux:
  • Глава 8. Пакеты: средства установки, системы управления, системы построения
  • Глава 9. Zenwalk: репозитории, пакеты, методы установки

    23 июля

    CITKIT.ru:

  • Все против всех. 64 vs 32, Intel vs AMD, tmpfs vs ext3
  • Две головы от Intel

  • Zenwalk: обзор штатных приложений (глава из книги "Zenwalk. Приобщение к Linux")

  • Нормально, Григорий...

    16 июля

    Обзоры журнала Computer:

  • Перспективы и проблемы программной инженерии в XXI веке
  • Большие хлопоты с большими объемами данных
  • Перспективы наноэлектроники

    CITKIT.ru:

  • Интермедия о лицензиях (А.Федорчук. "Zenwalk. Приобщение к Linux")

  • Есть ли будущее у KDE?

  • Linux в школе: альтернативный вариант в задачах

  • Шифр (приключения агента Никодима)

    10 июля

    CITKIT.ru:

  • Новые разделы книги А. Федорчука Zenwalk. Приобщение к Linux:
  • Интермедия вступительная. Linux или GNU/Linux? Как вас теперь называть?
  • Глава 5. Среда Xfce
  • Глава 6. Xfce: приложения и плагины

  • ZUR (Zenwalk User Repository) FAQ

    2 июля

  • Персистентность данных в объектно-ориентированных приложениях (С. Кузнецов)

    CITKIT.ru:

  • Новые разделы книги А. Федорчука Zenwalk. Приобщение к Linux:
  • Интермедия 1.2. Дорога к Zenwalk'у. Период бури и натиска
  • Интермедия 3.3. Немного о Linux'е и "железе"
  • Глава 4. Настройка: инструментами и руками
  • Интермедия 4.1. Zenpanel и конфиги: поиски корреляции

  • Интервью с Жан-Филиппом Гийоменом, создателем дистрибутива Zenwalk

  • Linux в школе: первые итоги (С. Голубев)

    25 июня

    CITKIT.ru:

  • Zenwalk. Приобщение к Linux (А. Федорчук)

  • Логика и риторика (С.Голубев)

  • Технология Tru64 AdvFS

  • Ханс Райзер предлагает отвести полицейских к телу Нины

    18 июня

  • Проекты по управлению данными в Google (Пересказ: С. Кузнецов)

    CITKIT.ru:

  • ОС и поддержка "железа": мифы и реальность (А. Федорчук)

  • Linux в школе: другие дистрибутивы

  • Пинок (С. Голубев)

    4 июня

  • Ландшафт области управления данными: аналитический обзор (С. Кузнецов)

    CITKIT.ru:

  • Linux в школе: слово заинтересованным лицам

  • SlackBuild: пакеты своими руками

  • Linux от компании Novell. Установка и обзор openSUSE Linux

    Все публикации >>>




  • IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

    Информация для рекламодателей PR-акции, размещение рекламы — тел. +7 495 4119920, ICQ 232284597 Пресс-релизы — pr@citcity.ru
    Послать комментарий
    Информация для авторов
    Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
    Copyright © 1997-2000 CIT, © 2001-2007 CIT Forum
    Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...