22.11.2007 10:14:31Как установить личный сертификат из командной строки Ответов: 35
LexSTV
Добрый день, уважаемые, у меня такой вопрос:
Создаю инсталляцию для пользователя, и крайне не хочется ему слать инструкцию по установке личного сертификата, связывания его с контейнером закрытых ключей и пр. Так вот собственно вопрос: Возможно ли установить личный сертификат из командной строки, если да, то как?
 
Ответы:
22.11.2007 13:02:51maxdm
http://cryptopro.ru/cryptopro/forum/view.asp?q=6567
22.11.2007 13:51:42LexSTV
Примного благодарен, а можно ли достать исходники этой замечательной проги (cryptCP) чтоб я в свою dllлину мог этот блок включить?
23.11.2007 11:48:22LexSTV
Ответьте мне пожалуйста.
Я запускаю вашу утилиту cryptCp и на любую комнду она мне отвечает, что лицензия просрочена
вот пример

D:\project\cryptCP>cryptcp.exe -CSPcert -df test2.cer -nochain -norev

CryptCP 3.16 (c) "Крипто-Про", 2002-2006.
Утилита командной строки для защиты данных.
Ошибка: Лицензия просрочена. (0x20000325)

При запуске КриптоПро написано
"Время действия лицензии - постоянная"
23.11.2007 11:52:21LexSTV
версия криптоПро КС1 3.0.3300.2
23.11.2007 11:54:16LexSTV
версия cryptCp 3.16
23.11.2007 11:56:14Tatianka
У КриптоПро CSP и cryptcp разные лицензии. Лицензия на cryptcp приобретается отдельно. У нас на сайте вместе с дистрибутивом cryptcp выложена лицензия для тестирования сроком на 30 дней.
23.11.2007 12:11:48LexSTV
Штука платная, понял.
Тогда такой вопрос,
не могли бы Вы вложить кусок кода, который предлагает пользователю выбрать контейнер с приватным ключом и возвращал бы String имя этого контейнета для того, чтоб я мог добавить его в загружаемый сертификат таким образом

CRYPT_KEY_PROV_INFO kpi;
ZeroMemory(&kpi, sizeof(kpi));
kpi.pwszContainerName = pszKeyContainerName;
kpi.pvParam = 0;
kpi.rgProvParam = 0;
wszProvName = NULL;
kpi.dwProvType = PROV_RSA_FULL;
kpi.dwFlags = 0;
kpi.dwKeySpec = AT_SIGNATURE;// AT_KEYEXCHANGE;
kpi.cPro
if (!CertSetCertificateContextProperty(pCertContext,CERT_KEY_PROV_HANDLE_PROP_ID, 0, &kpi))
{
CryptDestroyKey(hKeyUser);
CryptReleaseContext(hCertProvUser, 0);
}

Возможно, что я глобально в чем-то ошибаюсь, ибо новичек. Поправьте, если что не так
23.11.2007 12:24:52LexSTV
где pszKeyContainerName как раз полученное имя ключегого контейнера
23.11.2007 12:24:53LexSTV
где pszKeyContainerName как раз полученное имя ключегого контейнера
23.11.2007 12:24:54LexSTV
где pszKeyContainerName как раз полученное имя ключегого контейнера
23.11.2007 13:00:13и снова LexSTV
как я понял, почитав эту переписку:
------------------------------------------
Антон Ответить

Спасибо за поправку. Но у меня вопрос: могу ли я с помощью API Crypto Pro получить информацию о считывателях ключевых носителей, которые использует Crypto Pro в данный момент (и получить информацию об ключевых контейнерах в ключевом носителе).
Могу ли я с помощью вашего API получить пользовательский интерфейс наподобие интерфейса "Control Panel->Crypto Pro CSP->Service->View Certificates in container"?

Ответы:

13.10.2003 10:04:41 Я сам (Антон)

Ладно, я понял. Информацию о текущих считывателях ключевых контейнеров можно почерпнуть из реестра. Но как узнать какие ключевые контейнеры мне доступны (например, со смарт-карты)?

13.10.2003 10:28:16 uri

По первому вопросу: API нету. :-( Нуно вашему софту лезть в реестр и в разделе HKEY_LOCAL_MACHINE\SOFTWARE\Crypto Pro\Cryptography\CurrentVersion\KeyDevices перечислены считыватели ключевых носителей. Если у раздела есть подраздел, то значит КриптоПро CSP настроен на него :-)
А список ключевых контейнеров, доступных в системе можно посмотреть с помощью функции CPGetProvParam(), которая производит возвращение параметров криптопровайдера. Одним из параметров является PP_ENUMCONTAINERS. Посмотрите в руководстве программиста КриптоПро CSP ЖТЯИ.00005-01 90 03 подробнее.

По второму вопросу: Посмотрите наш проект в качестве примера. а в нем csptest -property -cinstall. Возьмите за основу...
---------------------------------------------

Понял, что готовой приблуды нет, запустив которую она выкинет окно с контейнерами и вернет наименование?
23.11.2007 13:43:06Kirill Sobolev
В CSP есть, вылезет окно аналогичное тому, которое вылезает в контрольной панели - CryptGetProvParam(.., PP_SELECT_CONTAINER, ..)
23.11.2007 18:33:51LexSTV
не поверите, но CryptGetProvParam не поддерживает параметр PP_SELECT_CONTAINER пишу такой код, на что мне выдается ошибка "Указан неправильный тип"
вот код:

HCRYPTPROV m_hCryptProv = NULL;
if (!CryptAcquireContext(&m_hCryptProv, NULL, NULL, 75, 0))
{
m_hCryptProv = NULL;
THROW_GLE();
} // Петр 30.10.2007

DWORD szKeyContainerLen = 0;
LPTSTR szKeyContainer = NULL;
if (!CryptGetProvParam (m_hCryptProv, PP_SELECT_CONTAINER, NULL, &szKeyContainerLen, 0)) {
THROW_ERR(_T("Ошибка при получении размера имени контейнера секретного ключа"), GetLastError());
}
if (szKeyContainerLen <= 0){
THROW_ERR(_T("Не удалось получить имя контейнера секретного ключа"), GetLastError());
}
szKeyContainer = (LPTSTR)ALLOC(szKeyContainerLen);
if (!CryptGetProvParam (m_hCryptProv, PP_SELECT_CONTAINER, (LPBYTE)szKeyContainer, &szKeyContainerLen, 0)) {
THROW_ERR(_T("Ошибка при получении имени контейнера секретного ключа"), GetLastError());
}
26.11.2007 11:27:25Kirill Sobolev
А сам параметр у вас определен?
#define PP_SELECT_CONTAINER 110
26.11.2007 15:24:32LexSTV
да пробовал и так. Все равно пишет, что "Указан неправильный тип." вот код, которым я пытаюсь это сделать

Указан неправильный тип.
26.11.2007 15:25:16LexSTV
вот код

DWORD szKeyContainerLen = 0;
LPTSTR szKeyContainer = NULL;
if (!CryptGetProvParam (m_hCryptProv, 110, NULL, &szKeyContainerLen, 0)) {
THROW_ERR(_T("Ошибка при получении размера имени контейнера секретного ключа"), GetLastError());
}
if (szKeyContainerLen <= 0){
THROW_ERR(_T("Не удалось получить имя контейнера секретного ключа"), GetLastError());
}
szKeyContainer = (LPTSTR)ALLOC(szKeyContainerLen);
if (!CryptGetProvParam (m_hCryptProv, 110, (LPBYTE)szKeyContainer, &szKeyContainerLen, 0)) {
THROW_ERR(_T("Ошибка при получении имени контейнера секретного ключа"), GetLastError());
}
26.11.2007 17:12:31Kirill Sobolev
максимальную длину имени надо определять так
CryptGetProvParam (m_hCryptProv, PP_ENUMCONTAINERS, NULL, &szKeyContainerLen, CRYPT_FIRST))
28.11.2007 10:52:13LexSTV
день добры, спасибо, контейнер выбирает...
но к сертификату не привязывает закрытый ключ.
помоему я что-то забыл сделать
я делаю так. передаю в функцию контекст сертификата, получаю имя контейнера, добавляю его в сертификат через CertSetCertificateContextProperty
и добавляю сертификат в хренилище.
Что я пропустил?


вот код всей моей функции

{
HCRYPTPROV m_hCryptProv = NULL;
if (!CryptAcquireContext(&m_hCryptProv, NULL, NULL, 75, 0))
{
m_hCryptProv = NULL;
THROW_GLE();
} // Петр 30.10.2007

DWORD szKeyContainerLen = 0;
LPTSTR szKeyContainer = NULL;

if (!CryptGetProvParam (m_hCryptProv, PP_ENUMCONTAINERS, NULL, &szKeyContainerLen, CRYPT_FIRST)) {
THROW_ERR(_T("Ошибка при получении размера имени контейнера секретного ключа"), GetLastError());
}
if (szKeyContainerLen <= 0){
THROW_ERR(_T("Не удалось получить имя контейнера секретного ключа"), GetLastError());
}
szKeyContainer = (LPTSTR)ALLOC(szKeyContainerLen);
if (!CryptGetProvParam (m_hCryptProv, 110, (LPBYTE)szKeyContainer, &szKeyContainerLen, 0)) {
THROW_ERR(_T("Ошибка при получении имени контейнера секретного ключа"), GetLastError());
}
CRYPT_KEY_PROV_INFO kpi;
ZeroMemory(&kpi, sizeof(kpi));
kpi.pwszContainerName = (LPWSTR)szKeyContainer;
kpi.pwszProvName = NULL;
kpi.dwProvType = PROV_RSA_FULL;
kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
kpi.dwKeySpec = AT_KEYEXCHANGE;
kpi.cProvParam = 0;
kpi.rgProvParam = 0;
if (!CertSetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, 0, &kpi))
{
THROW_ERR(_T("Ошибка при получении имени контейнера секретного ключа"), GetLastError());
}
if (!CertAddCertificateContextToStore(m_hCertStore, pCert, CERT_STORE_ADD_REPLACE_EXISTING, NULL)){
THROW_ERR(_T("Ошибка при добавлении сертификата в личное хранилище"), GetLastError());
}
28.11.2007 10:52:14LexSTV
день добры, спасибо, контейнер выбирает...
но к сертификату не привязывает закрытый ключ.
помоему я что-то забыл сделать
я делаю так. передаю в функцию контекст сертификата, получаю имя контейнера, добавляю его в сертификат через CertSetCertificateContextProperty
и добавляю сертификат в хренилище.
Что я пропустил?


вот код всей моей функции

{
HCRYPTPROV m_hCryptProv = NULL;
if (!CryptAcquireContext(&m_hCryptProv, NULL, NULL, 75, 0))
{
m_hCryptProv = NULL;
THROW_GLE();
} // Петр 30.10.2007

DWORD szKeyContainerLen = 0;
LPTSTR szKeyContainer = NULL;

if (!CryptGetProvParam (m_hCryptProv, PP_ENUMCONTAINERS, NULL, &szKeyContainerLen, CRYPT_FIRST)) {
THROW_ERR(_T("Ошибка при получении размера имени контейнера секретного ключа"), GetLastError());
}
if (szKeyContainerLen <= 0){
THROW_ERR(_T("Не удалось получить имя контейнера секретного ключа"), GetLastError());
}
szKeyContainer = (LPTSTR)ALLOC(szKeyContainerLen);
if (!CryptGetProvParam (m_hCryptProv, 110, (LPBYTE)szKeyContainer, &szKeyContainerLen, 0)) {
THROW_ERR(_T("Ошибка при получении имени контейнера секретного ключа"), GetLastError());
}
CRYPT_KEY_PROV_INFO kpi;
ZeroMemory(&kpi, sizeof(kpi));
kpi.pwszContainerName = (LPWSTR)szKeyContainer;
kpi.pwszProvName = NULL;
kpi.dwProvType = PROV_RSA_FULL;
kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
kpi.dwKeySpec = AT_KEYEXCHANGE;
kpi.cProvParam = 0;
kpi.rgProvParam = 0;
if (!CertSetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, 0, &kpi))
{
THROW_ERR(_T("Ошибка при получении имени контейнера секретного ключа"), GetLastError());
}
if (!CertAddCertificateContextToStore(m_hCertStore, pCert, CERT_STORE_ADD_REPLACE_EXISTING, NULL)){
THROW_ERR(_T("Ошибка при добавлении сертификата в личное хранилище"), GetLastError());
}
28.11.2007 14:03:29LexSTV
Как я понимаю, нужно указать еще и ключ, хотя зачем, если контейнер с ним есть?
но как это сделать?
оч прошу, подскажите
28.11.2007 14:33:42Kirill Sobolev
>> kpi.pwszContainerName = (LPWSTR)szKeyContainer;
если у Вас программа не юникодная, должно быть честное преобразование ANSI-Unicode
>> kpi.pwszProvName = NULL;
имя провайдера тоже надо задать
>> kpi.dwProvType = PROV_RSA_FULL;
почему PROV_RSA_FULL? вроде речь шла о CryptoPro CSP
>> kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
а вот это как раз не нужно
ключ надо указывать т.к. в контейнере могут быть 2 ключа - подписи и обмена, а сертификат соответствует только одному из них.
29.11.2007 3:07:469zx6y1w4x9
ywodp4mw5ns4f3 <a href = http://www.436378.com/498888.html > 26wi5k6b06rq </a> [URL=http://www.541380.com/175932.html] 7k0iz975odbnlj [/URL] os13edl85nkwihlff
29.11.2007 3:08:009zx6y1w4x9
ywodp4mw5ns4f3 [URL=http://www.541380.com/175932.html] 7k0iz975odbnlj [/URL] os13edl85nkwihlff
29.11.2007 3:08:059zx6y1w4x9
ywodp4mw5ns4f3 http://www.336036.com/677339.html os13edl85nkwihlff
29.11.2007 3:08:109zx6y1w4x9
ywodp4mw5ns4f3 uu24ohc4kc4 os13edl85nkwihlff
29.11.2007 10:27:01LexSTV
Как я раскопал в wincryptEx
имя провайдера - CP_KC2_GR3410_2001_PROV_W
тип провайдера - PROV_GOST_2001_DH

Но каким образом указывать ключ, я найти не могу, не оставьте в беде, скажите чем и куда доваблять ключ
29.11.2007 10:36:46LexSTV
Речь идет о приват ключе
29.11.2007 10:49:13LexSTV
Поскольку фундаментальных знаний не имею в криптографии( точнее никаких) но по работе кровь из носу нужно сделать,
То правильно ли я понял, что ключ надо импортить функцией
CryptImportKey, и непостедственно в сертификат добавлять это его не нужно. Но тогда такой вопрос.
Где взять непосредственно файл ключа. Ведь считывателей может быть уйма, а функция CryptGetProvParam возвращает нам только имя контейнера (папки на считывателе) и путь к этому файлу как узнать?
29.11.2007 11:21:32LexSTV
Еще выяснил, что файлов ключей несколько в контейнере
header.key
masks.key
name.key
primary.key
primary2.key

какой из них в качестве блоба передавать функции CryptImportKey?
Може подскажете литературу, где я это все прочесть смогу?
29.11.2007 11:34:23Kirill Sobolev
Никакой путь к файл не надо указывать, ключ в CRYPT_KEY_PROV_INFO задается именем контейнера + типом ключа (подпись или обмен).
29.11.2007 12:25:04LexSTV
Супер бест!
Спасибо всем!
Я в долгу :-)
вот, если кому надо, функция, получающая сертифитат устанавливаем ему закрытый ключ

////////////////////////// мы начинаем кавеен
void _CryptoService::SetKeyCertificate(PCCERT_CONTEXT pCert){
HCRYPTPROV m_hCryptProv = NULL;
if (!CryptAcquireContext(&m_hCryptProv, NULL, NULL, 75, 0))
{
m_hCryptProv = NULL;
THROW_GLE();
} // Петр 30.10.2007

DWORD szKeyContainerLen = 0;
LPTSTR szKeyContainer = NULL;

if (!CryptGetProvParam (m_hCryptProv, PP_ENUMCONTAINERS, NULL, &szKeyContainerLen, CRYPT_FIRST)) {
CryptReleaseContext(m_hCryptProv,0);
THROW_ERR(_T("Ошибка при получении размера имени контейнера секретного ключа"), GetLastError());
}
if (szKeyContainerLen <= 0){
CryptReleaseContext(m_hCryptProv,0);
THROW_ERR(_T("Не удалось получить имя контейнера секретного ключа"), GetLastError());
}
szKeyContainer = (LPTSTR)ALLOC(szKeyContainerLen);
if (!CryptGetProvParam (m_hCryptProv, 110, (LPBYTE)szKeyContainer, &szKeyContainerLen, 0)) {
FREE(szKeyContainer);
CryptReleaseContext(m_hCryptProv,0);
THROW_ERR(_T("Ошибка при получении имени контейнера секретного ключа"), GetLastError());
}

CRYPT_KEY_PROV_INFO kpi;
ZeroMemory(&kpi, sizeof(kpi));
kpi.pwszContainerName = (LPWSTR)szKeyContainer;
kpi.pwszProvName = CP_KC2_GR3410_2001_PROV_W;
kpi.dwProvType = PROV_GOST_2001_DH;
kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
kpi.dwKeySpec = AT_KEYEXCHANGE|AT_SIGNATURE;
kpi.cProvParam = 0;
kpi.rgProvParam = 0;
if (!CertSetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, 0, &kpi))
{
FREE(szKeyContainer);
CryptReleaseContext(m_hCryptProv,0);
THROW_ERR(_T("Ошибка при получении имени контейнера секретного ключа"), GetLastError());
}
FREE(szKeyContainer);
if (!CertAddCertificateContextToStore(m_hCertStore, pCert, CERT_STORE_ADD_REPLACE_EXISTING, NULL)){
CryptReleaseContext(m_hCryptProv,0);
THROW_ERR(_T("Ошибка при добавлении сертификата в личное хранилище"), GetLastError());
}
CryptReleaseContext(m_hCryptProv,0);
}
29.11.2007 14:00:20Kirill Sobolev
>> kpi.dwKeySpec = AT_KEYEXCHANGE|AT_SIGNATURE;
Тут не должна быть сумма, нужно или AT_KEYEXCHANGE или AT_SIGNATURE - сертификат может ссылаться только на 1 ключ, т.к. в нем самом 1 ключ.
30.11.2007 17:05:01LexSTV
упс, но, как ни странно, все работает?!
я исходил из той логики, что этим сертификатом мы и шифруем, и подписываем, по этому написал сумму...
если я выберу какой-то один тип, то смогу ли я подписывать(или шифровать) этим же сертификатом?
30.11.2007 18:04:44Kirill Sobolev
Подписываете и шифруете Вы, все-таки, не сертификатом, а ключем. Ключ может быть как обмена, так и подписи. На ключе обмена можно шифровать и подписывать, на ключе подписи - только подписывать.
30.11.2007 18:54:53LexSTV
Отлично, спасибо, зничит исправлю на AT_KEYEXCHANGE