22.08.2005 17:07:43шифрование контейнер Ответов: 15
zebra
Здравствуйте. запутался совсем...
есть два удаленных компа, т.е. пользователя. У каждого пользователя по контейнеру с собственным private ключом и public ключем другого пользователя. Контейнеры в реестре.
Возможно ли шифрование файла на стороне А, а потом его дешифрование на стороне Б без импорта\експорта и траспортировки ключей по сети.
Ведь, на сколько я плохо понял, импорт\экспорт(точне функции CryptImportKey, CryptExportKey) нужны тогда, когда генериться новая пара ключем и требуется транспортировка ключей другому пользователю.

CryptGetUserKey - The CryptGetUserKey function retrieves a handle of one of a user’s two public/private key pairs.
хэндл одного ключа пары пользователя что ли? только одного?

Используя эту функцию, для получения ключа при шифровании, смогу ли я, используя её же при дешифровании, расшифровать текст?
 
Ответы:
22.08.2005 17:15:26Василий
Стороне А надо иметь ОТКРЫТЫЙ ключ стороны Б (например, из сертификата Б). Тогда, с использованием своего личного закрытого ключа и чужого открытого ключа можно зашифровать сообщение. Для расшифровки используются две другие половинки - закрытый ключ получателя и открытый ключ отправителя.
Пример см. http://www.cryptopro.ru/CryptoPro/forum/myforum.asp?q=4
22.08.2005 17:39:04zebra
"Стороне А надо иметь ОТКРЫТЫЙ ключ стороны Б (например, из сертификата Б). Тогда, с использованием своего личного закрытого ключа и чужого открытого ключа можно зашифровать сообщение. Для расшифровки используются две другие половинки - закрытый ключ получателя и открытый ключ отправителя."

Это понятно... Почему просто нельзя вытянуть из контйнера А нужные ключи, зашифровать. А на стороне Б вытащить из контейнера тоже нужные ключи и расшифровать...
Необходимо ли генерировать сессионый ключ?! мне, по-видимому, не нужно пользоваться GenKey для генерации сессионых и долговременных ключей.. так?
НЕ понятно, если все ключи у меня есть в контейнерах, нужные для шифрования\дешифрования - то как соответсвующую пару вытаскивать из контейнера на стороне А и Б соответственно..

22.08.2005 18:14:36Василий
"...Почему просто нельзя вытянуть из контйнера А нужные ключи..." - потому что их там нет. В контейнере А одна ключевая пара (свои закрытый\открытый ключи ГОСТ Р 34.10-94 или ГОСТ Р 34.10-2001), предназначенные для создания ключа Диффи-Хеллмана (с участием открытого ключа другой стороны), на котором зашифрован сессионный ключ, которым зашифрованы данные (т.к. шифрование данных всегда производится на симметричном сессионном ключе по ГОСТ 28147-89).
22.08.2005 18:32:15zebra
сессионный ключ получается надо всегда передавать вместе с зашифрованным файлом?
а на приемной стороне расшифрововать сессионный ключ (с помощью тоже пары Диффи-Хеллмана), и с помощью сессионого расшиврованного расшифрововать данные...
так выходит?

если да,, то возможен ли какой вариант, чтобы передавать только зашифрованный файл (данные), без передачи сессионого ключа, или нет?

Спасибо.
23.08.2005 11:04:30Василий
По схеме - всё правильно. Сессионный ключ шифруется на ключе Диффи-Хеллмана и передаётся получателю.

А если не передавать сессионный ключ - это можно. Вот только расшифровать данные никто не сможет.
23.08.2005 12:49:41zebra
Поясните пожалуйста, правильно ли я понял.
в примере http://www.cryptopro.ru/CryptoPro/forum/myforum.asp?q=4

в самом начале почти:
if(!CPExportKey(hCreateContainer, hExchangeKeyPair, hTempKey, PUBLICKEYBLOB, 0, pExchPubKeyBlob , &dwBlobLen)) {
printf("Error %x during CPExportKey!\n", GetLastError());
goto done;
}
-получаем блоб открытого ключа из контейнера, причем шифруем по ключу hTempKey=0. Это для чего? чтобы сохранить ключь в неизменном виде?


// создаём Agreed Key
// Вырабатывается ключ парной связи
if(!CPImportKey(hCreateContainer, pbRecipientPubKeyBlob, dwLenRecipientPubKeyBlob,
hExchangeKeyPair, 0, &hUserAgreeKey)) {
goto done;
}
-это иморт из блоба ПУБЛИЧНОГО ключа получателя? расшифровываем его с помощью нашего hExchangeKeyPair...hUserAgreeKey- это какой ключ: открытый или закрытый?

далее
if(!CPExportKey(hCreateContainer ,
hSessionKey, hUserAgreeKey, SIMPLEBLOB , 0, pbSecretKeyBlob, &dwBlobLen)) {
goto done;
}
получаем зашифрованный сешн-ки по hUserAgreeKey? так что ли выходит, что бы потом pbSecretKeyBlob передать что ли?
23.08.2005 14:02:14Василий
Первый экспорт на нулевом ключе - просто для записи открытого ключа в блоб для передачи его другой стороне дле последующего использования. Открытый ключ можно получить и из сертификата.
Далее импорт - это создание ключа Диффи-Хеллмана (просто нет отдельной функции для создания ключа ДХ).
Второй экспорт - шифрование сессионного ключа для передачи результата (блоба) получателю.

А вообще, Вам обязательно использовать низкоуровневые функции? Можно применять более высокоуровневые, которые сами умеют создавать сессионный ключ, шифровать его на ключе ДХ, передавать вместе с сообщением приёмной стороне и аналогично - при расшифровании. Пример - CryptEncryptMessage. См. также http://www.cryptopro.ru/CryptoPro/test/sample2_0.zip
23.08.2005 17:51:50paul
CRYPT_ENCRYPT_MESSAGE_PARA EncryptPara;
EncryptPara.ContentEncryptionAlgorithm.pszObjId

не очень понятно, какие идентификаторы алгоритмов можно писать сюда...
например, можно, CALG_G28147 (Идентификатор алгоритма шифрования по ГОСТ 28147 89. )

а остальные, вроде: CALG_GR3410(Идентификатор алгоритма имитозащиты по ГОСТ 28147 89.) или CALG_GR3410EL (Идентификатор алгоритма ЭЦП по ГОСТ Р 34.10-2001. ) - они не для шифрования же? или тоже подойдут?

у меня работа может идти с несколькими криптопровайдерами:
-"Crypto-Pro GOST R 34.10-94 Cryptographic Service Provider";
-"Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider";

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

спасибо
23.08.2005 17:57:20zebra
скажите, пожалуйста, а почему при шифровке 74 байтов, получаю 1579... зашифрованных. Это нормально?

RYPT_ENCRYPT_MESSAGE_PARA EncryptPara;
ZeroMemory(&EncryptPara, sizeof(EncryptPara));

EncryptPara.cbSize = sizeof(EncryptPara);
EncryptPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
EncryptPara.ContentEncryptionAlgorithm.pszObjId =
(LPSTR)CertAlgIdToOID(CALG_G28147 );
EncryptPara.hCryptProv = hProv;

char string[] = "TeqweqweqweqeqwestTeqweqweqweqeqwestTeqweqweqweqeqwestTeqweqweqweqeqwest";
DWORD count;
int len=strlen(string);

if(!CryptEncryptMessage(&EncryptPara, 1, &pCertContext, (BYTE*)string,
len, NULL, &count))
{
//Error("CryptEncryptMessage");
return 0;
}

BYTE *encrypted;
encrypted = (BYTE*)malloc(count);

if(!CryptEncryptMessage(&EncryptPara, 1, &pCertContext, (BYTE*)string,
strlen(string), encrypted, &count))
{
//Error("CryptEncryptMessage");
return 0;
}

//std::cout << "Encryption completed" << std::endl;

CertCloseStore(hSystemStore, 0);
CryptReleaseContext(hProv, 0);
24.08.2005 12:39:54Василий
Нормально.
Собственно шифртекст занимает 80 байт (74 байта дополняются до размера, кратного 8 байтам).
Но, при формировании сообщения используется ASN-кодирование. Плюс заголовки, зашифрованный сессионный ключ, синхровектор, сертификат отправителя (бОльшая часть сообщения в данном случае), сведения о сертификате получателя.
24.08.2005 13:58:53zebra
Подскажите, пожалуйста, как понять почему CryptSignMessage падает...
При первом вызове...

DWORD count;

CRYPT_SIGN_MESSAGE_PARA SignPara;
ZeroMemory(&SignPara, sizeof(SignPara));



SignPara.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
SignPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
SignPara.pSigningCert = pCertContext;
SignPara.HashAlgorithm.pszObjId = (LPSTR)CertAlgIdToOID(CALG_G28147);
SignPara.HashAlgorithm.Parameters.cbData = NULL;
SignPara.cMsgCert = 1;
SignPara.rgpMsgCert = &pCertContext;
SignPara.cAuthAttr = 0;
SignPara.dwInnerContentType = 0;
SignPara.cMsgCrl = 0;
SignPara.cUnauthAttr = 0;
SignPara.dwFlags = 0;
SignPara.pvHashAuxInfo = NULL;
SignPara.rgAuthAttr = NULL;

char string1[] = "Test";
const BYTE* DataArray[] = {(BYTE*)string1 };
DWORD SizeArray[1];
SizeArray[0] = strlen(string1)+1;
count = 0;

if(!CryptSignMessage(&SignPara, false, 1, DataArray, SizeArray,
NULL, &count))
{
DWORD res;
res=GetLastError();
if(res==ERROR_INVALID_HANDLE)
return 1;
if(res==ERROR_INVALID_PARAMETER)
return 2;
if(res==ERROR_MORE_DATA)
return 3;
if(res==NTE_BAD_FLAGS)
return 4;
if(res==NTE_BAD_KEY)
return 5;
if(res==NTE_BAD_KEY_STATE)
return 6;
if(res==NTE_BAD_PUBLIC_KEY)
return 7;
if(res==NTE_BAD_TYPE)
return 8;
if(res==NTE_BAD_UID)
return 9;
if(res==NTE_NO_KEY)
return 10;

if(ERROR_MORE_DATA==res)
return 0;
if (E_INVALIDARG ==res)
return 0;
if(CRYPT_E_NO_KEY_PROPERTY==res)
return 0;
//Error("CryptSignMessage");
return 0;
}

ни одной из этих ошибок не возвращает,..
как понять, что не так?

спасибо.
24.08.2005 15:04:58zebra
разобрался. Алгоритм хеширования не тот...

не очень понятно как работает CryptEncryptMessage.
HCERTSTORE hSystemStore;
HCRYPTPROV hProv;
CString strContainerName("2a905e2b-76cc-4683-a625-23d5d7fd04a6");
CString strCSPName="Crypto-Pro GOST R 34.10-94 Cryptographic Service Provider";//("Crypto-Pro GOST R 34.10-94 Cryptographic Service Provider");//71 //"Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider"
DWORD dwCSPType=71;


if (!CryptAcquireContext(&hProv, strContainerName,strCSPName,dwCSPType , 0))
{
::MessageBox(NULL,"CryptAcquireContext ERROR:01 ",
NULL,MB_OK|MB_ICONERROR);
return 0;
}

if (!(hSystemStore = CertOpenSystemStore(hProv, "MY")))
{
::MessageBox(NULL,"CertOpenSystemStore ERROR: can not open.",
NULL,MB_OK|MB_ICONERROR);
return 0;
}
PCCERT_CONTEXT pCertContext = NULL;
DWORD cbNameLen;
LPSTR sz;
CString strCertSubjectANSI="Коробовский";


//convert subjectname from ANSI into UNICODE
int val=MultiByteToWideChar(CP_ACP, 0, strCertSubjectANSI, -1,NULL, 0);
LPWSTR lpCertSubjectUnicode;//=NULL;

val=val*2;//++;
lpCertSubjectUnicode = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, val);
val=MultiByteToWideChar(CP_ACP, 0, strCertSubjectANSI, -1, lpCertSubjectUnicode, val);
//end convertion

//find certificate with the subject as lpCertSubjectUnicode
pCertContext=CertFindCertificateInStore (hSystemStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0, CERT_FIND_SUBJECT_STR,
lpCertSubjectUnicode , NULL);
if(!pCertContext)
{
::MessageBox(NULL,"CertFindCertificateInStore ERROR: can not find cert.",
NULL,MB_OK|MB_ICONERROR);
return 0;
}


CRYPT_ENCRYPT_MESSAGE_PARA EncryptPara;
ZeroMemory(&EncryptPara, sizeof(EncryptPara));

EncryptPara.cbSize = sizeof(EncryptPara);
EncryptPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
EncryptPara.ContentEncryptionAlgorithm.pszObjId =
(LPSTR)CertAlgIdToOID(CALG_G28147 );
EncryptPara.hCryptProv = hProv;


if(!CryptEncryptMessage(&EncryptPara, 1, &pCertContext, lpBuffer,
dwFileSize, NULL, &count))
{
::MessageBox(NULL,"CryptEncryptMessage ERROR: 1.",
NULL,MB_OK|MB_ICONERROR);
return 0;
}

BYTE *encrypted;
encrypted = (BYTE*)malloc(count);

if(!CryptEncryptMessage(&EncryptPara, 1, &pCertContext,lpBuffer,
dwFileSize, encrypted, &count))
{
::MessageBox(NULL,"CryptEncryptMessage ERROR: 2.",
NULL,MB_OK|MB_ICONERROR);
return 0;
}

WriteDataIntoFile("c:\\en.txt.",encrypted,count);

Вопрос:
-испульзуются ли для зашифровки private ключь в таком случае?
-пользователь, получивший зашиврованное сообщение не сможет расшифровать, не имея у себя к контейнере Public ключа отправителя, так?
25.08.2005 11:35:53Василий
Третий параметр ф-и CryptEncryptMessage - массив сертификатов получателей сообщений.
Только те, кто имеет сертификат (и секретный ключ) из этого списка, смогут расшифровать сообщение.
Для зашифрования используется свой закрытый ключ (из контейнера) и открытые ключи из сертификатов получателей. При расшифровании - используется открытый ключ отправителя (из сообщения) и сертификат + закр. ключ одного из получателей.

Можно зашифровать для себя. Тогда, как при зашифровании, так и при расшифровании нужен будет только свой сертификат и свой закрытый ключ.
25.08.2005 11:39:02zebra
Я понял, спасибо.
"Для зашифрования используется свой закрытый ключ (из контейнера) и открытые ключи из сертификатов получателей. При расшифровании - используется открытый ключ отправителя (из сообщения) и сертификат + закр. ключ одного из получателей.
"
Закрытый ключ, получается, автоматически используется CryptSignMessage?
25.08.2005 13:02:13Василий
Да, берётся ключевая пара AT_KEYEXCHANGE из контейнера, соответствующего хендлу hProv.

Кстати, можно не открывать его самостоятельно (CryptAcquireContext), а получить из сертификата (в случае, если он установлен в хранилище Личные с привязкой к ключу) - например, CertGetCertificateContextProperty с параметром CERT_KEY_PROV_HANDLE_PROP_ID