17573

Использование криптографического интерфейса Windows при разработке приложений. Шифрование и дешифрование данных

Лабораторная работа

Информатика, кибернетика и программирование

Лабораторная работа № 4.3 Тема: Использование криптографического интерфейса Windows при разработке приложений. Шифрование и дешифрование данных. Формирование и проверка ЭЦП. Управление доступом к контейнеру ключей. Цель: изучить принципы построения и использования Cr...

Русский

2013-07-04

1.22 MB

13 чел.

Лабораторная работа № 4.3

Тема: Использование криптографического интерфейса Windows при разработке приложений. Шифрование и дешифрование данных. Формирование и проверка ЭЦП. Управление доступом к контейнеру ключей.

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

Задание:

1) Ознакомиться с теоретическими положениями, посвященными принципам работы с криптографическим интерфейсом Windows.

2) Написать программу которая обеспечивает:

  •  шифрование содержимого файла «init.txt», дешифрование полученного криптотекста;
  •  подпись текстового файла и ее верификацию.
  •  

Структура отчета

  1.  Титульный лист.
  2.  Тема и цель работы.
  3.  Задание и номер варианта.
  4.  Краткие теоретические сведения.
  5.  Ход работы.
  6.  Выводы.

Теоретические сведения

Шифрование и расшифрование данных

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

BOOL CryptEncrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen); /* шифрование на сеансовом ключе с дескрип-тором hKey порции данных из буфера *pbData длиной dwBufLen fpdwDataLen - длина порции данных, после выполнения функции в эту переменную записывается фактическая длина зашифрованных данных); hHash=0, dwFlags=0, Final - признак последней порции шифруемых данных */

Для расшифрования зашифрованных с помощью сеансового ключа данных применяется функция CryptDecrypt: BOOL CryptDecrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen); /* расшифрование на сеансовом ключе с дескриптором hKey порции данных из буфера *pbData fpdwDataLen - длина порции данных, после выполнения функции в эту переменную записывается фактическая длина расшифрованных данных); hHash=0, dwFlags=0, Final - признак последней порции расшифровываемых данных */

Приведем примеры использования функций CryptoAPI для шифрования и расшифрования файла, имя которого содержится в константе SECFILE (константа TMPFILE содержит имя файла с зашифрованными данными).

Пример 1 . В приложении на языке С++: /* подключение заголовочного файла с определением классов ввода-вывода*/ 

#include <fstream.h>

BYTE Buf[128]; /* буфер для ввода-вывода данных, длина которого должна быть кратна длине блока при использовании блочного шифрования (обычно 64 бит) */

fstream TmpFile1,TmpFile2; // файловые переменные

DWORD Len; // длина порции зашифрованных (расшифрованных) данных

/*получение дескриптора контейнера ключей криптопровайдера и создание сеансового ключа с дескриптором hKey из введенной пользователем ключевой фразы (см. пример из предыдущей лабораторной работы) */

// открытие исходного файла в двоичном режиме 

TmpFile2.open(SECFILE, ios::in | ios::binary);

// создание файла для шифротекста

TmpFile1.open(TMPFILE, ios::out | ios::binary);

// цикл шифрования

do

{// чтение блока данных из исходного файла

TmpFile2.read((char*)Buf, sizeof(Buf));

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

Len=TmpFile2.gcount();

// шифрование блока данных в буфере

CryptEncrypt(hKey, 0, TmpFile2.eof(), 0, But, &Len, sizeof(Buf));

// запись зашифрованного блока в файл с шифротекстом

TmpFilel .write((const char*)Buf, Len);} while(!TmpFile2.eof());

// закрытие файлов с открытым текстом и шифротекстом

TmpFilel .close();

TmpFile2.close();

// удаление файла с открытым текстом

remove(SECFILE);

// разрушение сеансового ключа

CryptDestroyKey(h Key);

/* генерация сеансового ключа дескриптором hKey для расшифрования файла путем генерации его из введенной пользователем ключевой фразы */

// открытие файла с шифротекстом в двоичном режиме

TmpFilel .open(TMPFILE, ios::in | ios::binary);

// создание файла для расшифрованного текста

TmpFile2.open(SECFILE, ios::out | ios::binary);

// цикл расшифрования

do

{// чтение блока данных с шифротекстом

TmpFilel .read((char*)Buf, sizeof(Buf));

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

Len=TmpFile1 .gcount();

// расшифрование блока шифротекста

CryptDecrypt(hKey, 0, TmpFilel .eof(), 0, But, &Len);

// запись блока расшифрованных данных в файл

TmpFile2.write((const char*)Buf, Len);} while(!TmpFile1.eof());

// закрытие файлов

TmpFilel.close();

TmpFile2.close();

// удаление файла с шифротекстом

remove(TMPFILE);

// разрушение сеансового ключа

CryptDestroyKey(hKey);

// продолжение работы программы

II освобождение дескриптора криптопровайдера

 hProv CryptReleaseContexi(hProv,0);

Получение и проверка электронной цифровой подписи

После создания пары ключей ЭЦП возможен вызов функций CryptoAPI для получения и проверки электронной цифровой подписи под электронными документами.

Получение ЭЦП

Перед получением ЭЦП необходимо получить хеш-значение для подписываемых данных. Для этого должны использоваться ранее рассмотренные функции CryptCreateHash, CryptHashData и

CryptHashSessionKey. Затем вызывается функция CryptoAPI для получения ЭЦП:

BOOL CryptSignHash(HCRYPTHASH hHash, DWORD dwKeySpec, LPCTSTR sDescription, DWORD dwFlags, BYTE *pbSignature, DWORD *pdwSigLen); /* получение ЭЦП для хеш-значения с дескриптором hHash с помещением подписи в буфер *pbSignature длины *pdwSigLen (после выполнения функции в эту переменную записывается фактическая длина ЭЦП); dwKeySpec=AT_SIGNATURE, sDe-scription=NULL, dwFlags=0 */

Параметр dwKeySpec может также принимать значение AT_KEYEXCHANGE, что может потребоваться для обеспечения только целостности данных без прямого указания на их владельца (например, сеансового ключа). В этом случае для получения ЭЦП будет использован секретный ключ обмена сеансовыми ключами.

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

Проверка ЭЦП

Для проверки электронной цифровой подписи вначале также необходимо вычислить хеш-значение для электронного документа, чьи аутентичность и целостность проверяются. После этого вызывается функция CryptoAPI, выполняющая проверку ЭЦП: BOOL CryptVerifySignature(HCRYPTHASH hHash, BYTE *pbSignature, DWORD dwSigLen, HCRYPTKEY hPubKey, LPCTSTR sDescription, DWORD dwFlags); /* проверка ЭЦП из буфера длины dwSigLen для хеш-значения с дескриптором hHash с помощью открытого ключа hPubKey (sDescription=NULL или строка с описанием проверяемого документа, dwFlags=0); если данные, хеш-значение которых содержится в hHash, были после получения ЭЦП изменены или открытый ключ hPubKey не соответствует секретному ключу ЭЦП, то эта функция возвращает FALSE с кодом ошибки NTE__BAD_SIGNATURE от функции GetLastError */

Пример. Приведем пример получения и проверки ЭЦП для файла, имя которого запрашивается у пользователя, а ЭЦП помещается в файл с тем же именем и дополнительным расширением .sig. Пример составлен на языке С++ для системы программирования Borland С++ Builder:

#include <fstream.h>

AnsiString Source, Dest; // имена подписываемого файла и файла с ЭЦП

fstream TmpFilel, TmpFile2; // файловые переменные

HCRYPTPROV hProv=0; // дескриптор криптопровайдера

HCRYPTHASH hHash; // дескриптор хеш-значения HCRYPTKEY 

hPubKey=0; // дескриптор открытого ключа ЭЦП

BYTE Buf[128]; // буфер для чтения из хешируемого файла

DWORD Len; // длина прочитанных из файла данных

BYTE Sign[MAX_PATH]; // буфер для ЭЦП DWORD 

SigLen=sizeof(Sign); // длина ЭЦП II запрос и получение имени подписываемого файла

Source=lnputBox("Ввод имени подписываемого файла", "Имя файла:","");

// если имя не введено или файл с таким именем не существует, то выход

if(Source=="" || IFileExists(Source)) return;

// формирование имени файла с ЭЦП

Dest=Source + ".sig";

// получение дескриптора криптопровайдера

CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,0);

// создание пустого хеш-значения

CryptCreateHash(hProv,CALG_SHA,0,0,&hHash);

// открытие подписываемого файла

TmpFilel .open(Source.c_str(),ios::in|ios::binary);

// создание файла с ЭЦП

TmpFile2.open(Dest.c_str(),ios::out|ios::binary);

// цикл хеширования подписываемого файла

do {// чтение данных из файла в буфер

TmpFilel .read((char)Buf,sizeof(Buf));

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

Len=TmpFile1 .gcount();

// хеширование данных из буфера

CryptHashData(hHash,Buf,Len,0);} while(!TmpFile1 .eof()); // проверка создания ключей ЭЦП if(!CryptGetUserKey(hP,AT_SIGNATURE,&hPubKey))

/ / создание пары ключей ЭЦП, если они еще не созданы

if((unsigned)GetLastError()==NTE_NO_KEY) CryptGenKey(hProv, AT_SIGNATURE, 0, ShPubKey);

// получение ЭЦП

CryptSignHash(hHash,AT_SIGNATURE,NULL,0,Sign,&SigLen);

// запись ЭЦП в файл

TmpFile2.write((const char*)Sign,Sigl_en);

// закрытие файлов

TmpFilel .close();

TmpFile2.close();

// разрушение хеш-значения

CryptDestroyHash(hHash);

// запрос и получение имени подписанного файла

 Source=lnputBox("Ввод имени подписанного файла", "Имя файла:","");

// если имя не введено или файл с таким именем не существует, то выход

if(Source=="" || !FileExists(Source)) return;

// формирование имени файла с ЭЦП

 Dest=Source + ".sig";

// если файл с ЭЦП не существует, то выход 

if(!FileExists(Dest)) return;

// создание пустого хеш-значения

CryptCreateHash(hProv,CALG_SHA,0,0,&hHash);

// открытие проверяемого файла и файла с ЭЦП

TmpFilel .open(Source.c_str(),ios::in|ios::binary);

TmpFile2.open(Dest.c_str(),ios::in|ios::binary);

// цикл хеширования файла

do 

{// чтение данных из проверяемого файла в буфер

TmpFilel .read((char*)Buf,sizeof(Buf));

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

Len=TmpFile1 .gcount();

// хеширование данных из буфера

CryptHashData(hHash,Buf,Len,0);}

while(!TmpFile1 .eof());

// чтение ЭЦП из файла

TmpFile2.read(Sign,SigLen);

// проверка ЭЦП

if(!CryptVerifySignature(hHash,Sign,SigLen,hPubKey,NULL,0))

ShowMessage("noflnncb неверна!");

else ShowMessageCTIoflnncb верна!");

/ /  закрытие файлов

TmpFilel. close();

TmpFile2.close();

// разрушение хеш-значения

CryptDestroyHash(hHash);

// разрушение дескриптора открытого ключа ЭЦП

CryptDestroyKey(hPubKey);

// освобождение дескриптора криптопровайдера

CryptReleaseContext(hProv,0);

Возможно одновременное выполнение шифрования данных и получения ЭЦП для них, а также расшифрования и проверки ЭЦП. При этом перед шифрованием данных в приложении должно быть создано пустое хеш-значение с помощью функции CryptCreateHash, и полученный дескриптор должен быть указан в качестве значения параметра hHash при вызове функции CryptEncrypt. Хеширование данных в этом случае производится перед их шифрованием. После завершения процесса шифрования полученное хеш-значение может быть подписано с помощью функции CryptSignHash.

При выполнении одновременного расшифрования и хеширования данных первоначально также должно быть создано пустое хеш-значение с помощью функции CryptCreateHash, после чего полученный дескриптор должен быть указан в качестве значения параметра hHash при вызове функции CryptDecrypt. Данные вначале будут расшифровываться, а затем хешйроваться. Полученное хеш-значение может быть затем использовано для проверки ЭЦП с помощью функции CryptVerifySignature.

Получение хеш-значения

Существует возможность получения фактического хеш-значения до или вместо получения ЭЦП. Для этого предназначена рассмотренная ранее функция CryptoAPI CryptGetHashParam. С помощью еще одной функции CryptoAPI может быть получена ЭЦП для фактического хеш-значения. Для этого вначале создается пустое хеш-значение с помощью функции CryptCreateHash, а затем вызывается функция CryptSetHashParam:

BOOL CryptSetHashParam(HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD dwFlags); /* установка в хеш-значении с дескриптором hHash новых данных из буфера *pbData (dwParam= HP_HASHVAL); значение параметра dwFlags не используется и должно быть равно нулю*/

Размер буфера *pbData должен быть равен длине хеш-значения для используемой функции хеширования. После копирования данных в хеш-значение для него может быть получена ЭЦП с помощью функции CryptVerifySgnature.

Функция CryptSetHashParam может также использоваться для дополнительной настройки процесса хеширования при получении для сообщения кода аутентификации, основанного на хешировании (Hash-based Message Authentication Code, HMAC). В этом случае секретный ключ добавляется в хеш-значение до и (или) после хеширования сообщения. Значение параметра dwParam при этом должно быть равно HP_HMAC_INFO, а буфер *pbData содержать структуру типа HMACJNFO, состоящую из полей:

ALGJD HashAlgid; // код алгоритма хеширования

 BYTE* pblnnerString; // внутренняя строка (по умолчанию 64 байт 0x36)

DWORD cblnnerString; /* длина внутренней строки (если 0, то используется строка по умолчанию)*/

BYTE* pbOuterString; // внешняя строка (по умолчанию 64 байт 0х5С)

DWORD cbOuterString; /* длина внешней строки (если 0, то используется строка по умолчанию) */

Управление доступом к контейнеру ключей

В операционных системах Windows 2000/ХР/2003 контейнеры ключей подобно другим объектам (файлам и папкам на томах с файловой системой NTFS, разделам реестра, принтерам, процессам и потокам, файлам, отображаемым на оперативную память, и т.д.) имеют дескрипторы безопасности (security descriptors), позволяющие реализовать дискреционное управление доступом (discretionary access control, DAC) к этим объектам. При дискреционном управлении доступом разрешения на доступ к объекту определяются его владельцем (собственником, owner).

Состав дескриптора безопасности

Дескриптор безопасности объекта содержит:

  •  идентификатор безопасности (security identifier, SID) собственника данного объекта (первоначально собственником становится субъект, создавший этот объект);
  •  идентификатор безопасности первичной группы собственника объекта (операционная система Windows в настоящее время не использует это значение);
  •  дискреционный список управления доступом (discretionary access control list, DACL), управляемый собственником объекта и позволяющий назначать другим субъектам права доступа к данному объекту;
  •  системный список контроля доступа (system access control list, SACL), управляемый администратором и позволяющий вести аудит попыток доступа субъектов к данному объекту.

Дискреционный список управления доступом состоит из элементов управления доступом (access control entries, АСЕ), каждый из которых содержит:

  •  идентификатор безопасности субъекта, права доступа которого определяются данным элементом;
  •  маску доступа (access mask), задающую комбинацию прав (видов) доступа, определяемых данным элементом;
  •  признак разрешения или запрещения прав доступа, определенных маской доступа (тип элемента АСЕ);
  •  признак наследования определяемых элементом АСЕ прав доступа от родительского объекта.

Для получения и изменения дескриптора безопасности контейнера ключей могут применяться функции CryptoAPI CryptGet-ProvParam (см. предыдущую лабораторную работу) и CryptSetProvParam соответственно: BOOL CryptSetProvParam (HCRYPTPROV. hProv, DWORD dwParam, BYTE *pbData, DWORD dwFJags); /* изменение дескриптора безопасности (если dwParam=PP_KEYSET_SEC_DESCR/) контейнера ключей, открытого в криптопровайдере hProv, на значение из буфера *pbData; значение dwFlags определяет состав изменяемой части дескриптора безопасности и может быть комбинацией констант OWNER_SECURITY_INFORMATION, GROUP_SECURITY_INFORMATION, DACL_SECURITY_INFORMATION, SACL_SECURITY_INFORMATION */

Для извлечения информации из дескриптора безопасности контейнера ключей могут использоваться следующие функции Windows API:

BOOL GetSecurityDescriptorOwner (PSECURITY_DESCRIPTOR pSe-curityDescriptor, PSID *pOwner, LPBOOL IpbOwnerDefaulted); /* получение в *pOwner указателя на идентификатор безопасности собственника объекта, указатель на дескриптор безопасности которого записан в pSecurityDescriptor; в IpbOwnerDefaulted помещается признак того, что собственник объекта был назначен по умолчанию */

BOOL GetSecurityDescriptorDacI (PSECURITY_DESCRIPTOR pSecurityDescriptor, LPBOOL IpbDaclPresent, PACL *pDacl, LPBOOL IpbDa-cIDefaulted); /* получение в 'IpbDaclPresent признака существования дискреционного списка контроля доступа и в *pDacl - указателя на этот список из дескриптора безопасности объекта, указатель на который содержится в pSecurityDescriptor; в *IpbOwnerDefaulted помещается признак того, что DACL был назначен по умолчанию */

Получить информацию из DACL о правах доступа субъектов к контейнеру ключей можно с помощью функции Windows API: DWORD GetExplicitEntriesFromAcl (PACL pacl, PULONG pcCountO-fExplicitEntries, PEXPLICIT_ACCESS *pьstOfExplicitEntries); /* получение в pListOfExplicitEntries массива из *pcCountOfExplicitEntries элементов ACE, извлеченных из списка контроля доступа, на который указывает pacl; функция возвращает ERROR_SUCCESS в случае успешного завершения */

Для использования функции GetExplicitEntriesFromAcl в программе на С++ необходимо подключить заголовочный файл Aclapi.h (в программах для Borland С++ Builder это нужно сделать перед подключением файла vcl.h, а в программах для Microsoft Visual С++ - в файле stdafx.h).

Структура EXPLICIT_ACCESS состоит из полей: DWORD grfAccessPermissions; // маска доступа ACCESS_MODE grfAccessMode; /* тип ACE: GRANT_ACCESS (доступ разрешен), SET_ACCESS (установка новых разрешений на доступ, замещающих ранее существовавшие), DENY_ACCESS (доступ запрещен), REVOKE_ACCESS (отзыв существующих прав доступа) */

DWORD grflnheritance; /* признак наследования прав доступа: значение NOJNHERITANCE (права доступа не унаследованы) или комбинация значений, определяющих различные варианты наследования объектом прав доступа */ 

TRUSTEE Trustee; // информация о субъекте доступа

Структура TRUSTEE содержит поля:

PTRUSTEE pMultipleTrustee; // должно быть NULL

MULTIPLE_TRUSTEE_OPERATION    MultipleTrusteeOperation;    /* должно быть NO_MULTIPLE_TRUSTEE */

TRUSTEE_FORM TrusteeForm; /* тип значения поля ptstrName:

TRUSTEE_IS_SID (идентификатор безопасности субъекта) или TRUSTEE_IS_NAME (имя учетной записи субъекта) */

TRUSTEE_TYPE TrusteeType; /* тип субъекта: TRUSTEE_IS_USER (пользователь), TRUSTEE_IS_GROUP (группа), TRUSTEE_IS_COMPUTER (компьютер) и др.*/ 

LPTSTR ptstrName; /* идентификатор безопасности или имя учетной записи субъекта */

Для получения имени учетной записи по ее идентификатору безопасности предназначена функция Windows API LookupAccountSid:

BOOL LookupAccountSid(LPCTSTR IpSystemName, PSID Sid LPTSTR Name, LPDWORD cbName, LPTSTR DomainName! LPDWORD cbDomainName, PSID_NAME_USE peUse); /* получение в буфере Name длины cbNameимени учетной записи и в буфере DomainName длины cbDomainName имени ее домена для идентификатора безопасности, указатель на который задается параметром Sid; при успехе функция возвращает TRUE, в cbName и cbDomainName записываются истинные длины  имен учетной записи и домена, а в peUse - тип учетной записи: SidTypeUser (пользователь), SidTypeGroup (группа), SidTypeComputer (компьютер) и т.д.; параметр IpSystemName может содержать имя удаленного компьютера или NULL для ссылки на локальный компьютер */

Изменение прав доступа к контейнеру ключей

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

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

BOOL LookupAccountName (LPCTSTR IpSystemName, LPCTSTR IpAc-countName, PSID Sid, LPDWORD cbSid, LPTSTR DomainName, LPDWORD CbDomainName, PSID_NAME_USE peUse); /* функция отличается от функции LookupAccountSid тем, что использует имя учетной записи IpAccountName и возвращает в буфере Sid длиной *cbSid байт соответствующий идентификатор безопасности */

Чтобы создать новый дискреционный список контроля доступа из уже подготовленного массива структур EXPLICIT_ACCESS, следует воспользоваться системной функцией Windows SetEntrieslnAcl-DWORD SetEntrleslnAcI (ULONG cCountOfExplicitEntries PEXPLICIT_ACCESS pListOfExplicitEntries, PACL OldAcI, PACL *NewAcI); /* добавление cCountOfExplicitEntries элементов массива pListOfExplicitEntries в существующий DACL, на который указывает OldAcI, или в новый DACL, если OldAcI=NULL; при успехе функция возвращает ERROR_SUCCESS, а в *NewAcI помещается указатель на буфер с новым DACL */

Функция SetEntrieslnAcI определена в заголовочном файле Aclapi.h.

Создание нового дескриптора безопасности объекта и добавление к нему дискреционного списка контроля доступа возможны с помощью системных функций:

BOOL InitializeSecurityDescriptor (PSECURITY_DESCRIPTOR pSecuri-tyDescriptor, DWORD dwRevision); /* инициализация нового дескриптора безопасности в буфере, указатель на который будет записан в pSecurityDescriptor;dwRevision=SECURITY_DESCRIPTOR_REVISION */

BOOL SetSecurityDescriptorDacI (PSECURITYJDESCRIPTOR pSecu-rityDescriptor, BOOL bDaclPresent, PACL pDacI, BOOL bDaclDefaulted); /* установка (замещение) дискреционного списка контроля доступа, на который указывает pDacI в дескрипторе безопасности, на который указывает pSecurityDescriptor; если bDaclPresent=TRUE, то происходит обновление DACL (иначе этот список удаляется из дескриптора безопасности); если bDaclDefaulted=TRUE, то DACL получен с помощью механизма умолчания (иначе он точно определяет права доступа субъектов) */

Важно понимать разницу между пустым (не содержащим ни одного элемента) дискреционным списком контроля доступа и случаем, когда дескриптор безопасности не содержит DACL. В первом случае доступ к объекту запрещен всем, а собственник имеет право добавить элементы в DACL. Во втором случае любой субъект имеет полный доступ к объекту и, в том числе, возможность смены собственника.

Пример. Рассмотрим пример управления доступом к контейнеру ключей в программе на языке С++ для системы программирования Borland С++ Builder. Это будет продолжением примера, рассмотренного в предыдущей лабораторной работе. На глобальном уровне были определены следующие переменные:

DWORD Len; // длина буфера

char pTN[MAX_PATH]; //  буфер для получения строкового значения  

HCRYPTPROV hP=0; // дескриптор криптопровайдера

AnsiString Tmp; // строка для формируемого сообщения

 LPVOID MsgBuf; // указатель на буфер с сообщением об ошибке

После вывода сообщения о создании в контейнере пар асимметричных ключей ЭЦП и (или) обмена (см. пердыдущую лабораторную) добавим в обработку смены выбранного элемента в списке «Контейнеры ключей» следующий код:

AnsiString pN; /* буфер для получения и формирования строковой информации (был объявлен ранее в примере из лабораторной работы 4.2) */

BYTE SD[MAX_PATH]; // буфер для получения дескриптора безопасности

PSID pSID; // указатель на идентификатор безопасности субъекта

BOOL рВ,рВ2; // признаки получения запрошенной информации

char pDN[MAX_PATH]; // буфер для получения имени домена

 unsigned long Len2,Len3; // длины буферов

SID_NAME_USE pNU; // тип субъекта

PACL pAcI; // указатель на DACL

PEXPLICIT_ACCESS eAccess; // указатель на массив АСЕ

/ /  установка длины буфера

Len=sizeof(SD);

/* получение дескриптора безопасности для контейнера ключей с включением информации о его собственнике и DACL */

if(!CryptGetProvParam(hP,PP_KEYSET_SEC_DESCR,SD,&Len,

OWNER_SECURITY_INFORMATION |

DACL_SECURITY_INFORMATION))

/* вывод сообщения об ошибке при получении дескриптора безопасности */

{ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |

FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),

MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),

(LPTSTR) &MsgBuf,0,NULL);

ShowMessage(AnsiString(

'Ошибка при получении дескриптора безопасности \n")

+(char*)MsgBuf); LocalFree(MsgBuf);    }

else

// если дескриптор безопасности получен

{/ / получение идентификатора безопасности собственника

GetSecurityDescriptorOwner((void*)SD,&pSID,&pB);

if(pSID)

// если SID собственника контейнера ключей получен

{// установка длин буферов для получения имени учетной записи и домена

Len=sizeof(pTN); Len2=sizeof(pDN);

// получение имени учетной записи 

LookupAccountSid(NULL,pSID,pTN,&Len,pDN,&Len2,&pNU);

// добавление к сообщению информации об имени и домене собственника

Tmp=AnsiString("Bnan,eneu, контейнера ключей:\п")+

pDN+"\\"+pTN+" ";

/* определение и добавление к сообщению информации о типе учетной записи собственника */

switch(pNU)

{case SidTypeUser: Ттр+="(пользователь)";

break;

case SidTypeGroup: Tmp+="(rpynna)";

break;

case SidTypeComputer: Ттр+="(компьютер)";}

// получение DACL из дескриптора безопасности 

GetSecurityDescriptorDacl((void*)SD,&pB,&pAcl,&pB2);

if(pB) // если DACL получен

{// добавление к сообщению информации из DACL 

Ттр+="\n Права доступа к контейнеру ключей:\n";

// получение массива элементов управления доступом 

if(GetExplicitEntriesFromAcl(pAcl,&Len3,&eAccess)==ERROR_SUCCESS)

// если список элементов получен 

for(unsigned i=0;i<Len3;i++)

// извлечение информации о правах доступа из очередного АСЕ

if(eAccess[i].Trustee.TrusteeForm==TRUSTEEJS_SID) /* если субъект доступа задан своим идентификатором безопасности */

{ / / установка длин буферов для получения имен субъекта и домена

Len=sizeof(pTN); Len2=sizeof(pDN);

// получение имени субъекта по его идентификатору безопасности

LookupAccountSid(NULL,eAccess[i].Trustee.ptstrName,

pTN, &Len,pDN,&Len2,&pNU);

// начало формирования строки с правами субъекта 

Tmp+=AnsiString(pDN)+"\\"+pTN+"";

// определение прав доступа субъекта 

if(eAccess[i].grfAccessPermissions&GENERIC_ALL) pN=" А";

if(eAccess[i].grfAccessPermissions&GENERIC_EXECUTE)

pN+="X";

if(eAccess[i].grfAccessPermissions&GENERIC_WRITE)

pN+="W";

if(eAccess[i].grfAccessPermissions&GENERIC_READ)

pN+="R";

// добавление к сообщению информации о правах доступа

 Tmp+=pN+" ";

// определение и добавление к сообщению информации о типе АСЕ

if(eAccess[i].grfAccessMode==GRANT_ACCESS)

Ттр+="разрешено";

if(eAccess[i].grfAccessMode==DENY_ACCESS)

Ттр+="запрещено";

// добавление к сообщению информации о признаке наследования прав доступа

if(eAccess[i].grflnheritance!=NO_INHERITANCE)

Ттр+=" (унаследовано)";

// переход к следующей строке сообщения

Ттр+="\n";}

// вывод сообщения с информацией из дескриптора безопасности контейнера ключей

ShowMessage(Tmp);

// запрос пользователю о необходимости изменения прав доступа

if(Application->MessageBox("Отозвать доступ администраторов?",

"Доступ к контейнеру ключей", MB_YESNO|MBJCONQUESTION) ==IDYES)

/* если пользователь согласен на отзыв прав доступа членов группы администраторов */

{ EXPLICIT_ACCESS dAdmin;

// новый элемент списка DACL

TRUSTEE Admin; // группа локальных администраторов

PSID aSid; // указатель на буфер для идентификатора безопасности

SECURITY_DESCRIPTOR aSD; // дескриптор безопасности с новым DACL 

// подготовка информации о субъекте доступа

Admin. pMultipleTrustee=NULL;

Admin.MultipleTrusteeOperation= NO_MULTIPLE_TRUSTEE;

Admin.TrusteeType=TRUSTEEJS_GROUP;

Admin.TrusteeForm=TRUSTEE_IS_NAME;

// выделение памяти под буфер для SID

aSid=new BYTE[MAX_PATH];

// установка длин буферов для получения SID и имени домена

Len=MAX_PATH; Len2=sizeof(pDN);

// попытка получения SID для уточнения имени группы локальных администраторов

if(LookupAccouniName(NULL, "Администраторы",

aSid,&l_en,pDN,&Len2,&pNU)) { lstrcpy(pTN,pDN);

lstrcat(pTN,"\\AflMHHHCTpaTopbi");}

else

{ LookupAccountName(NULL,"Administrators",aSid,&Len, pDN,&Len2,&pNU);

 lstrcpy(pTN,pDN); lstrcat(pTN, "WAdm in istrators");}

// завершение формирования структуры с информацией о субъекте доступа

Admin. ptstrName=pTN; // подготовка элемента списка DACL 

// задание субъекта доступа

dAdmin.Trustee=Admin;

// задание маски доступа

dAdmin.grfAccessPermissions=GENERIC_ALL;

//задание типа АСЕ (отзыв прав доступа)

dAdmin.grfAccessMode=REVOKE_ACCESS;

// задание признака наследования прав доступа

dAdmin.grflnheritance=NO_INHERITANCE;

// слияние нового DACL с существующим списком

SetEntrieslnAcl(1,&dAdmin,pAcl,&pAcl);

// инициализация нового дескриптора безопасности

lnitializeSecurityDescriptor(&aSD, SECURITY_DESCRIPTOR_REVISION);

// добавление DACL к созданному дескриптору безопасности

SetSecurityDescriptorDacl(&aSD,TRUE,pAcl,FALSE);

// установка нового дескриптора безопасности для контейнера ключей

if(CryptSetProvParam(hP,PP_KEYSET_SEC_DESCR, (BYTE*)&aSD,

DACL_SECURITYJNFORMATION))

// вывод сообщения при успешном изменении прав доступа

ShowMessage("правa доступа успешно изменены.");

// освобождение буфера с новым DACL

if(pAcl) LocalFree(pAcl);

// освобождение буфера с идентификатором безопасности

delete aSid;}

// освобождение буфера для списка элементов DACL

if(eAccess) LocalFree(eAccess);}}}

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

Рис. 1. Примеры сообщений при управлении доступом к контейнеру

Контрольные вопросы

  1.  Какие функции использует CryptoAPI для шифрования данных?
  2.  Какая функция применяется для расшифровывания данных с помощью сеансового ключа?
  3.  Каким образом можно сформировать ЭЦП средствами CryptoAPI?
  4.  Каким образом происходит проверка ЭЦП с использованием средств CryptoAPI?
  5.  С помощью какой функции формируется хєш-значения?Что такое дескриптор безопасности?
  6.  Какие компоненты содержит дескриптор безопасности?
  7.  Дайте определение понятию дискреционный список.
  8.  Какие составляющие содержит элемент управления доступом дискреционного списка?
  9.  Каким образом можно осуществить полусить и измениьт дескриптор безопасности контейнера ключей?
  10.  С помощью какой функции CryptoAPI осуществляется извлечение информации из дескриптора безопасности?
  11.  Как изменить права доступа к контейнеру ключей?


 

А также другие работы, которые могут Вас заинтересовать

52611. Действия с десятичными дробями (запись, округление, сложение, вычитание) 27 KB
  Предметом усвоения являются общие способы действия способы решения класса задач. В дальнейшем общий способ действия конкретизируется применительно к частным случаям. На каждом последующем уроке конкретизируется и развивается уже освоенный способ действия.