Atom Лента - Форум КриптоПро - Тема:Вопрос начинающего о наличии ключа - 10Форум КриптоПро - Atom Лентаurn:https:--www-cryptopro-ru:AtomLenta:ForumKriptoPro:Tema:Voprosnachinajushchegoonalichiikljucha-10:1Copyright 2024 Форум КриптоПро2024-03-28T13:35:02Zhttps://www.cryptopro.ru/forum2/Images/YAFLogo.pngForum Adminhttps://www.cryptopro.ruforum@cryptopro.ruАндрей Т.https://www.cryptopro.ru/forum2/default.aspx?g=profile&u=51780&name=Андрей Т.Андрей Т.https://www.cryptopro.ru/forum2/default.aspx?g=profile&u=51780&name=Андрей Т.two_oceanshttps://www.cryptopro.ru/forum2/default.aspx?g=profile&u=36490&name=two_oceansАндрей Т.https://www.cryptopro.ru/forum2/default.aspx?g=profile&u=51780&name=Андрей Т.two_oceanshttps://www.cryptopro.ru/forum2/default.aspx?g=profile&u=36490&name=two_oceansАндрей Т.https://www.cryptopro.ru/forum2/default.aspx?g=profile&u=51780&name=Андрей Т.YetAnotherForum.NETurn:https:--www-cryptopro-ru:ftPosts:st1:meid100206:1Вопрос начинающего о наличии ключа<table class="content postContainer_Alt" width="100%"><tr><td>Огромное спасибо. Очень доходчиво, точно. Все получилось. <br /><br />Прочитал еще раз, с полученными знаниями, help по функции CryptAcquireContext и флагу CRYPT_VERIFYCONTEXT.<br />Пришел к выводу, что там написано именно то, что Вы объяснили. Но сделано там это так, что догадаться о правильном смысле нереально.<br />Ну или мой английский недостаточно хорош. <br /><br />Еще раз, спасибо.</td></tr></table>2019-02-12T12:49:22+03:002019-02-12T12:49:22+03:00Андрей Т.<table class="content postContainer_Alt" width="100%"><tr><td>Огромное спасибо. Очень доходчиво, точно. Все получилось. <br /><br />Прочитал еще раз, с полученными знаниями, help по функции CryptAcquireContext и флагу CRYPT_VERIFYCONTEXT.<br />Пришел к выводу, что там написано именно то, что Вы объяснили. Но сделано там это так, что догадаться о правильном смысле нереально.<br />Ну или мой английский недостаточно хорош. <br /><br />Еще раз, спасибо.</td></tr></table>urn:https:--www-cryptopro-ru:ftPosts:st1:meid100187:1Вопрос начинающего о наличии ключаНапример, можно импортировать информацию об открытом ключе из контекста сертификата<br /><div class="code"><strong>Код:</strong><div class="innercode"><pre class="line-numbers"><code class="language-markup">pKeyBlob:=@(pCertCont^.pCertInfo^.SubjectPublicKeyInfo);
if not CryptCheck(CryptImportPublicKeyInfo(hProv,X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,pKeyBlob, hPubKey),'CryptImportPublicKeyInfo') then res:=err_import_key else res:=0;</code></pre>
</div></div>Здесь я использую функцию CryptCheck, которая возвращает логический результат полученный в первом параметре, но если получено FALSE, то записывает название функции из второго параметра и значение результата GetLastError() в протокол. К слову, при некоторых операциях возвращается TRUE, но GetLastError меняется на ненулевое значение и это ненулевое значение может там висеть еще долго. Поэтому для отладки иногда приходится делать SetLastError(0) перед вызовом функции чтобы не оставалось кодов ошибок от прошлых операций. pCertCont - это контекст сертификата (возвращается функциями в виде указателя). hPubKey - дескриптор открытого ключа (он не подойдет там, где нужен закрытый ключ, но подойдет для проверки подписи). Есть еще функция CryptImportKey, но там принимается другой (более сложный) формат блоба ключа - к информации из сертификата надо еще дописать вначале 16 байт заголовка. По заголовку определяется вид и формат ключа, сам ключ может быть зашифрован, используется при обмене ключей и шифровании сообщений.2019-02-12T10:39:11+03:002019-02-12T10:39:11+03:00two_oceansНапример, можно импортировать информацию об открытом ключе из контекста сертификата<br /><div class="code"><strong>Код:</strong><div class="innercode"><pre class="line-numbers"><code class="language-markup">pKeyBlob:=@(pCertCont^.pCertInfo^.SubjectPublicKeyInfo);
if not CryptCheck(CryptImportPublicKeyInfo(hProv,X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,pKeyBlob, hPubKey),'CryptImportPublicKeyInfo') then res:=err_import_key else res:=0;</code></pre>
</div></div>Здесь я использую функцию CryptCheck, которая возвращает логический результат полученный в первом параметре, но если получено FALSE, то записывает название функции из второго параметра и значение результата GetLastError() в протокол. К слову, при некоторых операциях возвращается TRUE, но GetLastError меняется на ненулевое значение и это ненулевое значение может там висеть еще долго. Поэтому для отладки иногда приходится делать SetLastError(0) перед вызовом функции чтобы не оставалось кодов ошибок от прошлых операций. pCertCont - это контекст сертификата (возвращается функциями в виде указателя). hPubKey - дескриптор открытого ключа (он не подойдет там, где нужен закрытый ключ, но подойдет для проверки подписи). Есть еще функция CryptImportKey, но там принимается другой (более сложный) формат блоба ключа - к информации из сертификата надо еще дописать вначале 16 байт заголовка. По заголовку определяется вид и формат ключа, сам ключ может быть зашифрован, используется при обмене ключей и шифровании сообщений.urn:https:--www-cryptopro-ru:ftPosts:st1:meid100173:1Вопрос начинающего о наличии ключа<table class="content postContainer_Alt" width="100%"><tr><td>Благодарю Вас за столь развернутый ответ. Он сильно помог мне в понимании происходящего.<br />Ваши советы я обязательно учту. Использую JwaWinCrypt версии 1.13<br /><br />Однако, при указании AT_KEYEXCHANGE результат не изменился.<br />Позволю себе привести код процедуры и результат выполнения.<br /><div class="code"><strong>Код:</strong><div class="innercode"><pre class="line-numbers"><code class="language-markup">
procedure TForm1.GetProvData(ProvNum: Cardinal);
var
ProvName, ContainerName: string;
I, ProvType, NameLen, hProv, DataLen, impType, Flag, Error: Cardinal;
Vers: array[0..3] of Byte;
Key: HCRYPTKEY;
begin
if not CryptEnumProviders(ProvNum, nil, 0, ProvType, nil, NameLen) then Exit;
SetLength(ProvName, NameLen);
if not CryptEnumProviders(ProvNum, nil, 0, ProvType, PChar(ProvName), NameLen) then Exit;
Memo1.Lines.Append(' Len: '+IntToStr(NameLen)+' '+ProvName);
Memo1.Lines.Append(' Provider type: '+ ProvTypeToStr(ProvType));
if not CryptAcquireContext(hProv, nil, PChar(ProvName), ProvType, CRYPT_VERIFYCONTEXT {+ CRYPT_MACHINE_KEYSET}) then Exit;
DataLen := 4;
if not CryptGetProvParam(hProv, PP_VERSION, @Vers, DataLen, 0) then Exit;
Memo1.Lines.Append(' Version: ' + IntToStr(Vers[1]) + '.' + IntToStr(Vers[0]));
if not CryptGetProvParam(hProv, PP_IMPTYPE, @impType, DataLen, 0) then Exit;
Memo1.Lines.Append(' Type: ' + ImpTypeToStr(impType));
Flag := CRYPT_FIRST;
CryptGetProvParam(hProv, PP_ENUMCONTAINERS, nil, DataLen, Flag);
Memo1.Lines.Append('Контейнеры: (Max size of container name: '+IntToStr(DataLen)+')');
SetLength(ContainerName, DataLen+1);
while True do
begin
if not CryptGetProvParam(hProv, PP_ENUMCONTAINERS, PByte(PChar(ContainerName)), DataLen, Flag) then
begin
Error := Cardinal(GetLastError);
if Error <> ERROR_NO_MORE_ITEMS then
Memo1.Lines.Append(' get container state: '+SysErrorMessage(Integer(Error)));
break;
end;
Memo1.Lines.Add(#13+ContainerName);
Flag := CRYPT_NEXT;
if not CryptGetUserKey(hProv, {AT_SIGNATURE} AT_KEYEXCHANGE, Key) then
begin
Error := Cardinal(GetLastError);
Memo1.Lines.Append(' get key handle: '+SysErrorMessage(Integer(Error)));
Key := 0;
end else
Memo1.Lines.Append(' get key handle: success ('+IntToStr(Key)+')');
if Key <> 0 then
CryptDestroyKey(Key);
end;
CryptReleaseContext(hProv, 0);
end;
</code></pre>
</div></div><br /><br /><br /><div class="code"><strong>Код:</strong><div class="innercode"><pre class="line-numbers"><code class="language-markup">
Len: 60 Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider
Provider type: Code: 75
Version: 4.0
Type: Code: 8
Контейнеры: (Max size of container name: 513)
b6835a3e-4885-4831-b070-d98129d0665d
get key handle: Ключ не существует
</code></pre>
</div></div><br /></td></tr></table>2019-02-12T05:51:24+03:002019-02-12T05:51:24+03:00Андрей Т.<table class="content postContainer_Alt" width="100%"><tr><td>Благодарю Вас за столь развернутый ответ. Он сильно помог мне в понимании происходящего.<br />Ваши советы я обязательно учту. Использую JwaWinCrypt версии 1.13<br /><br />Однако, при указании AT_KEYEXCHANGE результат не изменился.<br />Позволю себе привести код процедуры и результат выполнения.<br /><div class="code"><strong>Код:</strong><div class="innercode"><pre class="line-numbers"><code class="language-markup">
procedure TForm1.GetProvData(ProvNum: Cardinal);
var
ProvName, ContainerName: string;
I, ProvType, NameLen, hProv, DataLen, impType, Flag, Error: Cardinal;
Vers: array[0..3] of Byte;
Key: HCRYPTKEY;
begin
if not CryptEnumProviders(ProvNum, nil, 0, ProvType, nil, NameLen) then Exit;
SetLength(ProvName, NameLen);
if not CryptEnumProviders(ProvNum, nil, 0, ProvType, PChar(ProvName), NameLen) then Exit;
Memo1.Lines.Append(' Len: '+IntToStr(NameLen)+' '+ProvName);
Memo1.Lines.Append(' Provider type: '+ ProvTypeToStr(ProvType));
if not CryptAcquireContext(hProv, nil, PChar(ProvName), ProvType, CRYPT_VERIFYCONTEXT {+ CRYPT_MACHINE_KEYSET}) then Exit;
DataLen := 4;
if not CryptGetProvParam(hProv, PP_VERSION, @Vers, DataLen, 0) then Exit;
Memo1.Lines.Append(' Version: ' + IntToStr(Vers[1]) + '.' + IntToStr(Vers[0]));
if not CryptGetProvParam(hProv, PP_IMPTYPE, @impType, DataLen, 0) then Exit;
Memo1.Lines.Append(' Type: ' + ImpTypeToStr(impType));
Flag := CRYPT_FIRST;
CryptGetProvParam(hProv, PP_ENUMCONTAINERS, nil, DataLen, Flag);
Memo1.Lines.Append('Контейнеры: (Max size of container name: '+IntToStr(DataLen)+')');
SetLength(ContainerName, DataLen+1);
while True do
begin
if not CryptGetProvParam(hProv, PP_ENUMCONTAINERS, PByte(PChar(ContainerName)), DataLen, Flag) then
begin
Error := Cardinal(GetLastError);
if Error <> ERROR_NO_MORE_ITEMS then
Memo1.Lines.Append(' get container state: '+SysErrorMessage(Integer(Error)));
break;
end;
Memo1.Lines.Add(#13+ContainerName);
Flag := CRYPT_NEXT;
if not CryptGetUserKey(hProv, {AT_SIGNATURE} AT_KEYEXCHANGE, Key) then
begin
Error := Cardinal(GetLastError);
Memo1.Lines.Append(' get key handle: '+SysErrorMessage(Integer(Error)));
Key := 0;
end else
Memo1.Lines.Append(' get key handle: success ('+IntToStr(Key)+')');
if Key <> 0 then
CryptDestroyKey(Key);
end;
CryptReleaseContext(hProv, 0);
end;
</code></pre>
</div></div><br /><br /><br /><div class="code"><strong>Код:</strong><div class="innercode"><pre class="line-numbers"><code class="language-markup">
Len: 60 Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider
Provider type: Code: 75
Version: 4.0
Type: Code: 8
Контейнеры: (Max size of container name: 513)
b6835a3e-4885-4831-b070-d98129d0665d
get key handle: Ключ не существует
</code></pre>
</div></div><br /></td></tr></table>urn:https:--www-cryptopro-ru:ftPosts:st1:meid100140:1Вопрос начинающего о наличии ключа<table class="content postContainer" width="100%"><tr><td>В контейнере могут присутствовать ключ подписи и ключ обмена - если вкратце: отличаются разрешенными операциями, ключу обмена разрешено больше, он может и подписывать тоже. Оба этих вида ключа могут быть в одном контейнере, но как правило при генерации ключа создается новый контейнер и поэтому (если конечно вручную не добавляли второй) в контейнере будет только один ключ - или подписи или обмена. Поэтому если есть строка ключ обмена присутствует, строка "ключ подписи отсутствует" обычное дело и не является ошибкой.<br /><br />Другой вопрос что от того, какой ключ в контейнере зависит какую константу нужно использовать при обращении к ключу. Например, если ключ подписи нужно ставить AT_SIGNATURE, если ключ обмена AT_KEYEXCHANGE. По описанному "ключ подписи отсутствует" похоже, что у Вас ключ обмена, тогда если указана константа AT_SIGNATURE, в этом случае криптопровайдер скажет, что в данном контейнере нет ключа (ключа подписи), а если указана AT_KEYEXCHANGE, то ключ (ключ обмена) найдется.<br /><br />Еще может быть имя контейнера указано неверно. Чтобы не путаться попробуйте открыть хранилище личные CertOpenSystemStore(0,'MY') перечислить сертификаты в хранилище CertEnumCertificatesInStore и получить ссылку на контейнер через CryptAcquireCertificatePrivateKey. Также функция возвратит нужную константу (AT_SIGNATURE или AT_KEYEXCHANGE) и из ссылки можно запросить имя контейнера CryptGetProvParam(hProv,PP_CONTAINER,...). А дальше по полученным точным данным можно обращаться к контейнеру и напрямую.<br /><br />К слову, в контейнере еще возможны симметричный ключ и ключ УЭК, но они не совместимы с ключами обмена и подписи. Это к тому что строка "симметричный ключ отсутствует" также вполне нормальна.<br /><br />Еще пара советов: отказаться от модулей идущих в составе дельфи (Crypt2, старые версии JwaWinCrypt) и скачать самый новый JwaWinCrypt. Функции уже изменились с тех пор и в старых версиях неверные объявления. Особенно это касается возможности обращаться из 64-разрядных программ: JwaWinCrypt в этом плане тоже не идеал, но заменить пару типов в начале проще чем рыскать по всему модулю. Если в новой версии какие-то функции испорчены (CryptStringToBinary), то они не работают, не нужно их пытаться исправить. Если захотите скопировать объявление в свой код - не забывайте stdcall.</td></tr></table>2019-02-11T12:06:09+03:002019-02-11T12:06:09+03:00two_oceans<table class="content postContainer" width="100%"><tr><td>В контейнере могут присутствовать ключ подписи и ключ обмена - если вкратце: отличаются разрешенными операциями, ключу обмена разрешено больше, он может и подписывать тоже. Оба этих вида ключа могут быть в одном контейнере, но как правило при генерации ключа создается новый контейнер и поэтому (если конечно вручную не добавляли второй) в контейнере будет только один ключ - или подписи или обмена. Поэтому если есть строка ключ обмена присутствует, строка "ключ подписи отсутствует" обычное дело и не является ошибкой.<br /><br />Другой вопрос что от того, какой ключ в контейнере зависит какую константу нужно использовать при обращении к ключу. Например, если ключ подписи нужно ставить AT_SIGNATURE, если ключ обмена AT_KEYEXCHANGE. По описанному "ключ подписи отсутствует" похоже, что у Вас ключ обмена, тогда если указана константа AT_SIGNATURE, в этом случае криптопровайдер скажет, что в данном контейнере нет ключа (ключа подписи), а если указана AT_KEYEXCHANGE, то ключ (ключ обмена) найдется.<br /><br />Еще может быть имя контейнера указано неверно. Чтобы не путаться попробуйте открыть хранилище личные CertOpenSystemStore(0,'MY') перечислить сертификаты в хранилище CertEnumCertificatesInStore и получить ссылку на контейнер через CryptAcquireCertificatePrivateKey. Также функция возвратит нужную константу (AT_SIGNATURE или AT_KEYEXCHANGE) и из ссылки можно запросить имя контейнера CryptGetProvParam(hProv,PP_CONTAINER,...). А дальше по полученным точным данным можно обращаться к контейнеру и напрямую.<br /><br />К слову, в контейнере еще возможны симметричный ключ и ключ УЭК, но они не совместимы с ключами обмена и подписи. Это к тому что строка "симметричный ключ отсутствует" также вполне нормальна.<br /><br />Еще пара советов: отказаться от модулей идущих в составе дельфи (Crypt2, старые версии JwaWinCrypt) и скачать самый новый JwaWinCrypt. Функции уже изменились с тех пор и в старых версиях неверные объявления. Особенно это касается возможности обращаться из 64-разрядных программ: JwaWinCrypt в этом плане тоже не идеал, но заменить пару типов в начале проще чем рыскать по всему модулю. Если в новой версии какие-то функции испорчены (CryptStringToBinary), то они не работают, не нужно их пытаться исправить. Если захотите скопировать объявление в свой код - не забывайте stdcall.</td></tr></table>urn:https:--www-cryptopro-ru:ftPosts:st1:meid100126:1Вопрос начинающего о наличии ключа<table class="content postContainer_Alt" width="100%"><tr><td>Добрый день.<br />В компании возникла необходимость работать с ЭЦП.<br />Используется Delphi 2006, установлен CryptoPro 4.0, носитель ключа Рутокен.<br /><br />При использовании CryptoApi все функции, работающие с ключами возвращают ошибку "нет ключа" или "ключ не существует".<br />Тестирование ключевого контейнера с помощью CryptoPro CSP выдает, в том числе, строку "Ключ подписи отсутствует".<br />Однако, регистрация ключа на сайте с использованием плагина Криптопро, для работы с ЭЦП, прошла успешно.<br /><br />Вопрос. Что я делаю неправильно? Где понимаю неправильно?<br /><br />И заранее прошу извинить, я еще путаюсь немного с терминологией.</td></tr></table>2019-02-11T07:25:55+03:002019-02-11T07:25:55+03:00Андрей Т.<table class="content postContainer_Alt" width="100%"><tr><td>Добрый день.<br />В компании возникла необходимость работать с ЭЦП.<br />Используется Delphi 2006, установлен CryptoPro 4.0, носитель ключа Рутокен.<br /><br />При использовании CryptoApi все функции, работающие с ключами возвращают ошибку "нет ключа" или "ключ не существует".<br />Тестирование ключевого контейнера с помощью CryptoPro CSP выдает, в том числе, строку "Ключ подписи отсутствует".<br />Однако, регистрация ключа на сайте с использованием плагина Криптопро, для работы с ЭЦП, прошла успешно.<br /><br />Вопрос. Что я делаю неправильно? Где понимаю неправильно?<br /><br />И заранее прошу извинить, я еще путаюсь немного с терминологией.</td></tr></table>