| ||||
| ||||
Здравствуйте. запутался совсем... есть два удаленных компа, т.е. пользователя. У каждого пользователя по контейнеру с собственным private ключом и public ключем другого пользователя. Контейнеры в реестре. Возможно ли шифрование файла на стороне А, а потом его дешифрование на стороне Б без импорта\експорта и траспортировки ключей по сети. Ведь, на сколько я плохо понял, импорт\экспорт(точне функции CryptImportKey, CryptExportKey) нужны тогда, когда генериться новая пара ключем и требуется транспортировка ключей другому пользователю. CryptGetUserKey - The CryptGetUserKey function retrieves a handle of one of a user’s two public/private key pairs. хэндл одного ключа пары пользователя что ли? только одного? Используя эту функцию, для получения ключа при шифровании, смогу ли я, используя её же при дешифровании, расшифровать текст? | ||||
Ответы: | ||||
| ||||
Стороне А надо иметь ОТКРЫТЫЙ ключ стороны Б (например, из сертификата Б). Тогда, с использованием своего личного закрытого ключа и чужого открытого ключа можно зашифровать сообщение. Для расшифровки используются две другие половинки - закрытый ключ получателя и открытый ключ отправителя. Пример см. http://www.cryptopro.ru/CryptoPro/forum/myforum.asp?q=4 | ||||
| ||||
"Стороне А надо иметь ОТКРЫТЫЙ ключ стороны Б (например, из сертификата Б). Тогда, с использованием своего личного закрытого ключа и чужого открытого ключа можно зашифровать сообщение. Для расшифровки используются две другие половинки - закрытый ключ получателя и открытый ключ отправителя." Это понятно... Почему просто нельзя вытянуть из контйнера А нужные ключи, зашифровать. А на стороне Б вытащить из контейнера тоже нужные ключи и расшифровать... Необходимо ли генерировать сессионый ключ?! мне, по-видимому, не нужно пользоваться GenKey для генерации сессионых и долговременных ключей.. так? НЕ понятно, если все ключи у меня есть в контейнерах, нужные для шифрования\дешифрования - то как соответсвующую пару вытаскивать из контейнера на стороне А и Б соответственно.. | ||||
| ||||
"...Почему просто нельзя вытянуть из контйнера А нужные ключи..." - потому что их там нет. В контейнере А одна ключевая пара (свои закрытый\открытый ключи ГОСТ Р 34.10-94 или ГОСТ Р 34.10-2001), предназначенные для создания ключа Диффи-Хеллмана (с участием открытого ключа другой стороны), на котором зашифрован сессионный ключ, которым зашифрованы данные (т.к. шифрование данных всегда производится на симметричном сессионном ключе по ГОСТ 28147-89). | ||||
| ||||
сессионный ключ получается надо всегда передавать вместе с зашифрованным файлом? а на приемной стороне расшифрововать сессионный ключ (с помощью тоже пары Диффи-Хеллмана), и с помощью сессионого расшиврованного расшифрововать данные... так выходит? если да,, то возможен ли какой вариант, чтобы передавать только зашифрованный файл (данные), без передачи сессионого ключа, или нет? Спасибо. | ||||
| ||||
По схеме - всё правильно. Сессионный ключ шифруется на ключе Диффи-Хеллмана и передаётся получателю. А если не передавать сессионный ключ - это можно. Вот только расшифровать данные никто не сможет. | ||||
| ||||
Поясните пожалуйста, правильно ли я понял. в примере 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 передать что ли? | ||||
| ||||
Первый экспорт на нулевом ключе - просто для записи открытого ключа в блоб для передачи его другой стороне дле последующего использования. Открытый ключ можно получить и из сертификата. Далее импорт - это создание ключа Диффи-Хеллмана (просто нет отдельной функции для создания ключа ДХ). Второй экспорт - шифрование сессионного ключа для передачи результата (блоба) получателю. А вообще, Вам обязательно использовать низкоуровневые функции? Можно применять более высокоуровневые, которые сами умеют создавать сессионный ключ, шифровать его на ключе ДХ, передавать вместе с сообщением приёмной стороне и аналогично - при расшифровании. Пример - CryptEncryptMessage. См. также http://www.cryptopro.ru/CryptoPro/test/sample2_0.zip | ||||
| ||||
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"; ведь при работе с каждым нужны наверное определенные алгоритмы, как узнать , какие? спасибо | ||||
| ||||
скажите, пожалуйста, а почему при шифровке 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); | ||||
| ||||
Нормально. Собственно шифртекст занимает 80 байт (74 байта дополняются до размера, кратного 8 байтам). Но, при формировании сообщения используется ASN-кодирование. Плюс заголовки, зашифрованный сессионный ключ, синхровектор, сертификат отправителя (бОльшая часть сообщения в данном случае), сведения о сертификате получателя. | ||||
| ||||
Подскажите, пожалуйста, как понять почему 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; } ни одной из этих ошибок не возвращает,.. как понять, что не так? спасибо. | ||||
| ||||
разобрался. Алгоритм хеширования не тот... не очень понятно как работает 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 ключа отправителя, так? | ||||
| ||||
Третий параметр ф-и CryptEncryptMessage - массив сертификатов получателей сообщений. Только те, кто имеет сертификат (и секретный ключ) из этого списка, смогут расшифровать сообщение. Для зашифрования используется свой закрытый ключ (из контейнера) и открытые ключи из сертификатов получателей. При расшифровании - используется открытый ключ отправителя (из сообщения) и сертификат + закр. ключ одного из получателей. Можно зашифровать для себя. Тогда, как при зашифровании, так и при расшифровании нужен будет только свой сертификат и свой закрытый ключ. | ||||
| ||||
Я понял, спасибо. "Для зашифрования используется свой закрытый ключ (из контейнера) и открытые ключи из сертификатов получателей. При расшифровании - используется открытый ключ отправителя (из сообщения) и сертификат + закр. ключ одного из получателей. " Закрытый ключ, получается, автоматически используется CryptSignMessage? | ||||
| ||||
Да, берётся ключевая пара AT_KEYEXCHANGE из контейнера, соответствующего хендлу hProv. Кстати, можно не открывать его самостоятельно (CryptAcquireContext), а получить из сертификата (в случае, если он установлен в хранилище Личные с привязкой к ключу) - например, CertGetCertificateContextProperty с параметром CERT_KEY_PROV_HANDLE_PROP_ID | ||||