Вопрос по Win Crypt API, блочное шифрование

Тема в разделе "WASM.CRYPTO", создана пользователем ubil, 20 окт 2006.

  1. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    Я хочу научиться шифровать данные произвольной длины с помощью функции CryptEnctypt и Public ключа извлеченного из сертификата. Можно найти примеры работы с этой функцией, но там везде шифруются короткие строки типа "test" или "111111111". Но вот когда я пробовал зашифровать что-то размером порядка 128 байт - функция выдавала ошибку "неправильная длина". Внимательно почитав в мсдн описание функции, я понял, что нужно знать максимальный размер блока для блочного шифрования - его я нахожу с помошью функции CryptGetKeyParam, он как раз и есть 128байт(1024бит). Но, как я уже написал при заявленной длине исходных данных в 128 байт функция CryptEnctypt выдает все ту же ошибку, вне зависимомти от параметра "Final". Далее, мне удалось выяснить, что данные длиной в 128-11 байт тем же образом шифруются/расшифровываются нормально.
    Хотелось бы узнать, что я не так делаю, как это можно сделать как можно правильнее и откуда берется число 11байт в этой проблеме?:)
     
  2. Mescalito

    Mescalito New Member

    Публикаций:
    0
    Регистрация:
    31 мар 2005
    Сообщения:
    78
    Адрес:
    Харьков
    В 1-м номере за 2002 год RSDN описывалась подобная проблема. И неявно делался вывод: мелкомягкие - м*ки
     
  3. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    Спасибо:) Нашел я ту статью. И вправду шутники они... Хоть бы в MSDN хоть что-нибудь про это написали(про то, что максимальная длина блока для шифрования равна KP_BLOCKLEN-11 (или (KP_BLOCKLEN/(8..11)))). Слава Богу, что те жуткие предупреждения касаются вроде бы как только .NET.
     
  4. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    а читать msdn пробовали ?
    The Microsoft Enhanced Cryptographic Provider supports direct encryption with RSA public keys and decryption with RSA private keys. The encryption uses PKCS #1 Type 2 padding. On decryption, this padding is verified. The length of plaintext data that can be encrypted with a call to CryptEncrypt with an RSA key is the length of the key modulus minus eleven bytes. The eleven bytes is the chosen minimum for PKCS #1 padding.
     
  5. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    Ой:) Вот это место скорее всего не читал, хотя может и пробегал глазами, и, возможно если бы там было бы написано "11", а не "одиннадцать", то обратил бы непременно на это внимание.

    Здесь, где я читал описания и вправду нет никакого слова "eleven":
    msdn.microsoft.com/library/default.asp?url=/library/en-us/wcesecurity5/html/wce50lrfcryptencrypt.asp

    в отличие от:
    msdn.microsoft.com/library/default.asp?url=/library/en-us/seccrypto/security/cryptencrypt.asp
     
  6. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    Все же, кто-нибудь, расскажите, пожалуйста, как узнавать точно сколько байтов padding резервирует данный алгоритм? Эта константа, 11, берется откуда-то программно? Соглано приведенному примечанию, получается, что можно забить на параметр Final и всегда считать его TRUE, правильно? Если в любом случае используются padding байты, то я вообще не вижу смысла в этом параметре. А еще я не могу понять, почему KP_BLOCKLEN выдает какое-то хреново значение 128 байт шифрованного текста, когда я его могу итак получить вызвав функцию CryptEncrypt с нулевым указателем??? В то время как прозрачно нигде не приводится точное значение максимального размера блока нешифрованных данных.
     
  7. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    ubil
    возможно имеет смысл использовать функцию более высокого уровня - CryptEncryptMessage - будет проще с данными переменной длины.
    Либо вот это: ms-help://MS.MSDNQTR.2004OCT.1033/seccrypto/security/example_c_program_encrypting_a_file.htm
     
  8. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    infern0, Спасибо за совет. А самое главное, спасибо всем что обратили мое внимание на это магическое число 11 в документации. infern0, ели ты про
    msdn.microsoft.com/library/en-us/seccrypto/security/example_c_program_encrypting_a_file.asp
    то я этот пример еще раньше смотрел, но там не смотря на то, что используется CryptEncrypt в коде вообще не учитываются эти 11 байт. И пример немного не мой - мне надо зашифровывать с помощью ключа из заданного сертификата, а там в примере ключ генерится с помощью спец. функции. Походу если бы мне другой сертификат попался, то я бы мог еще нескоро узнать про эти 11 байт:)
     
  9. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    CryptEncryptMessage снимет кучу проблем в этом случае, поверь :)
     
  10. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    Похоже что так. Но вот что-то я не могу найти как там можно указать, что я хочу зашифровывать именно private ключом из сертификата. Или там это так по умолчанию?
     
  11. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    шифруется всегда открытым ключем сертификата получателя. А расшифровать он сможет только своим закрытым ключем. Т.е. шифровать может кто угодно, а вот расшифровывать только обладатель закрытого ключа. Сам сертификат - это по сути подписанный открытый ключ, закрытый же хранится отдельно.
     
  12. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    Пон. Но вот если возникает такая ситуация, что нужно зашифровать private ключом, а сертификат имеет в себе и private, и public ключи, это можно сделать не добавляя лишних вызовов? Или тут уже без функции CryptEncrypt, CryptAquireContext и пр. не обойтись?
     
  13. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    приватным ключем можно либо расшифровывать либо подписывать, либо ручками через cryptencrypt (хотя может и не проканает, я лично не пробовал)

    ps: если шифровать закрытым ключем по алгоритму RSA то расшифровать это дело сможет любой, т.к. открытый ключ свободно доступен с сертификатом. Называется такой режим - ЭЦП. Сдается мне что ты задачу изначально неверно ставишь.
     
  14. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    Это была гипотетическая задача:)
    А реальную я с самого начала вроде корректно сформулировал:
    Есть сертификат в хранилище, в нем - public ключ. Надо зашифровать строку байт размером почти наверняка большим чем размер ключа. Что меня останавливает, так это то, что в функцию CryptEncryptMessage надо послать параметр типа идентификатор алгоритма, который нужно вытащить из дескриптора ключа... Мне это представляется немного большим гемором чем поблочная шифровка с помощью CryptEncrypt. Или я может чета не догоняю?:)
     
  15. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    infern0
    Слушай, вот я проде все что надо делаю, параметры все заполняю, причем в отладчике вижу что присваиваются корректные значения... Но CryptEncryptMessage выдает ошибку "UNKNOWN ALGORITHM". Вот код:
    Код (Text):
    1.     PCCRYPT_OID_INFO            pCryptOIDInfo;
    2.     CRYPT_ENCRYPT_MESSAGE_PARA  encryptParams;
    3.     CRYPT_ALGORITHM_IDENTIFIER  encryptAlgorithm;
    4.     size_t                      encryptAlgorithmSize;
    5.     BYTE                        buff[MAX_PATH];
    6.     BYTE                        alg_params[MAX_PATH];
    7.  
    8. /*---------------       SETTING ALGORITHM IDENTIFICATOR     -------------------------------------------*/
    9.     encryptAlgorithmSize = sizeof(encryptAlgorithm);
    10.     memset(&encryptAlgorithm,0,encryptAlgorithmSize);
    11.  
    12.     dwBufferLen=MAX_PATH;
    13.     CryptGetKeyParam(hRsaKey,KP_ALGID,buff,&dwBufferLen,NULL);
    14.     if(dwBufferLen==0)
    15.     {
    16.         MessageBox(NULL,_T("Can't obtain algorithm identificator"),_T("Error"),NULL);
    17.         goto exit;
    18.     }
    19.     pCryptOIDInfo = CryptFindOIDInfo(CRYPT_OID_INFO_ALGID_KEY,(void*)buff,0);
    20.     encryptAlgorithm.pszObjId = (LPSTR)pCryptOIDInfo->pszOID;
    21.     memset(alg_params,0,sizeof(alg_params));
    22.     dwBufferLen=MAX_PATH;
    23.     CryptGetKeyParam(hRsaKey,KP_IV,alg_params,&dwBufferLen,NULL);
    24.     if(dwBufferLen==0)
    25.     {
    26.         encryptAlgorithm.Parameters.cbData = 0;
    27.         encryptAlgorithm.Parameters.pbData = NULL;
    28.     } else {
    29.         encryptAlgorithm.Parameters.cbData = dwBufferLen;
    30.         encryptAlgorithm.Parameters.pbData = alg_params;
    31.         }
    32. /*------------------------------------------------------------------------------------------------------*/
    33.  
    34.     memset(&encryptParams,0,sizeof(encryptParams));
    35.  
    36.     encryptParams.cbSize = sizeof(encryptParams);
    37.     encryptParams.ContentEncryptionAlgorithm = encryptAlgorithm;
    38.     encryptParams.dwMsgEncodingType = MY_ENCODING_TYPE;
    39.     encryptParams.hCryptProv = hMSProv;        // Выделил провайдер по информации о ключе из сертификата
    40. /*  encryptParams.pvEncryptionAuxInfo = NULL;
    41.     encryptParams.dwFlags = NULL;
    42.     encryptParams.dwInnerContentType = NULL;*/
    43.  
    44.     cData = strlen(string)+1 + 256; //длина ключа - 256 байт
    45.  
    46.     pData = new BYTE[cData];
    47.     memset(pData, 0, cData);
    48.  
    49.     if (!CryptEncryptMessage(   &encryptParams,
    50.                                 1,
    51.                                 &pCertCtx,
    52.                                 (const BYTE*)string,
    53.                                 strlen(string)+1,
    54.                                 pData,
    55.                                 &cData))
    56.     {
    57.         err = GetLastError();
    58.         switch(err)
    59.         {
    60.         case ERROR_MORE_DATA:
    61.             MessageBox(NULL,_T("ERROR_MORE_DATA"),NULL,NULL);
    62.             break;
    63.         case E_INVALIDARG:
    64.             MessageBox(NULL,_T("E_INVALIDARG"),NULL,NULL);
    65.             break;
    66.         case CRYPT_E_UNKNOWN_ALGO:
    67.             MessageBox(NULL,_T("CRYPT_E_UNKNOWN_ALGO"),NULL,NULL);
    68.             break;
    69.         default:
    70.             MessageBox(NULL,_T("Default"),NULL,NULL);
    71.             break;
    72.         }    
    73.     }
    Что в нем не так? Алгоритм - szOID_RSA_RSA. Заранее спасибо
     
  16. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    вот это не так. Микрософтовский криптопровайдер не поддерживает шифрование RSA. Попробуй szOID_RSA_RC4. Тогда алгоритм шифрования будет RC4, а сеансовый ключ для него будет по RSA зашифрован.

    ps:
    ContentEncryptionAlgorithm
    CRYPT_ALGORITHM_IDENTIFIER structure that contains the object identifier (OID) of the encryption algorithm. The chosen hCryptProv CSP must support the encryption algorithm.
     
  17. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    pps:

    Код (Text):
    1. The default provider name is Microsoft Strong Cryptographic Provider
    2.  
    3.  
    4.                Enumerating the supported algorithms
    5.  
    6.      Algid      Bits      Type        Name         Algorithm
    7.                                      Length          Name
    8.     ________________________________________________________
    9.     00006602h    128     Encrypt       4           RC2
    10.     00006801h    128     Encrypt       4           RC4
    11.     00006601h    56      Encrypt       4           DES
    12.     00006609h    112     Encrypt       13          3DES TWO KEY
    13.     00006603h    168     Encrypt       5           3DES
    14.     00008004h    160     Hash          6           SHA-1
    15.     00008001h    128     Hash          4           MD2
    16.     00008002h    128     Hash          4           MD4
    17.     00008003h    128     Hash          4           MD5
    18.     00008008h    288     Hash          12          SSL3 SHAMD5
    19.     00008005h    0       Hash          4           MAC
    20.     00002400h    1024    Signature     9           RSA_SIGN
    21.     0000a400h    1024    Exchange      9           RSA_KEYX
    22.     00008009h    0       Hash          5           HMAC
     
  18. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    Так я вроде и провайдер специально выбираю какой в сертификате прописан... Вот более подробный код(я кое-что исправил, укоротил процедуру получения идентификатора алгоритма)
    Код (Text):
    1.         pCertCtx = NULL;    //В хранилище вообще всего один сертификат
    2.         while(pCertCtx=CertEnumCertificatesInStore(hCertStore,pCertCtx))
    3.     {
    4.         if (!(::CertGetCertificateContextProperty(pCertCtx, CERT_KEY_PROV_INFO_PROP_ID, NULL, &cbData)))
    5.         {
    6.             DWORD   err = ::GetLastError();
    7.  
    8.         }
    9.         if(!(pbData = (BYTE*) malloc(cbData)))
    10.         {
    11.             DWORD   err = ::GetLastError();
    12.  
    13.         }
    14.         if (!(::CertGetCertificateContextProperty(pCertCtx, CERT_KEY_PROV_INFO_PROP_ID, pbData, &cbData)))
    15.         {
    16.             DWORD   err = ::GetLastError();
    17.         }
    18.         pKeyProvInfo = (CRYPT_KEY_PROV_INFO*)pbData;
    19.         break;
    20.     }
    21.  
    22.     if(!::CryptAcquireContextW( &hMSProv,
    23.                             (LPCWSTR)pKeyProvInfo->pwszContainerName,
    24.                         (LPCWSTR)pKeyProvInfo->pwszProvName, //Microsoft Base Provider v1.0
    25.                         pKeyProvInfo->dwProvType,
    26.                         0)  )
    27.     {
    28.         err = ::GetLastError();
    29.         MessageBox(NULL,_T("Can't aquuire certificate crypto-provider"),_T("Error"),NULL);
    30.         goto exit;
    31.     }
    32.  
    33.     //crypt by public key
    34.     char    string[PLAIN_STRING_LEN+1];
    35.     DWORD   plainTextLen;
    36.     for(int i=0;i<PLAIN_STRING_LEN;i++)
    37.     {
    38.         string[i] = '1';
    39.     }
    40.     string[PLAIN_STRING_LEN] = 0;
    41.  
    42.     CRYPT_ENCRYPT_MESSAGE_PARA  encryptParams;
    43.  
    44.     memset(&encryptParams,0,sizeof(encryptParams));
    45.    
    46.     encryptParams.cbSize = sizeof(encryptParams);
    47.     encryptParams.ContentEncryptionAlgorithm = pCertCtx->pCertInfo->SubjectPublicKeyInfo.Algorithm;
    48.     encryptParams.dwMsgEncodingType = MY_ENCODING_TYPE;
    49.     encryptParams.hCryptProv = hMSProv;
    50.  
    51.     plainTextLen = strlen(string)+1;
    52.     cData = plainTextLen+256;//0
    53.  
    54.     pData = new BYTE[cData+32];
    55.     memset(pData, 0, cData+32);
    56.  
    57.     if(!CryptEncryptMessage(&encryptParams,
    58.                             1,
    59.                             &pCertCtx,
    60.                             (BYTE*)string,
    61.                             plainTextLen,
    62.                             pData,
    63.                             &cData))
    64.     {
    65.         err = GetLastError(); // error: #define CRYPT_E_UNKNOWN_ALGO             _HRESULT_TYPEDEF_(0x80091002L)
    66.     }
     
  19. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    еще раз - для шифрования нужно использовать алгоритм типа "Encrypt". А алгоритм RSA в провайдерах микрософт имеет тип "Signature / Exchange".
    SubjectPublicKeyInfo.Algorithm - это алгоритм, использованный для создания публичного ключа сертификата, и совсем необязательно что этим алгоритмом можно именно шифровать данные.
    ы ?

    ps: ты пробовал передать szOID_RSA_RC4 ? работает ?
     
  20. ubil

    ubil New Member

    Публикаций:
    0
    Регистрация:
    7 ноя 2004
    Сообщения:
    203
    Адрес:
    ODESSA:)
    infern0
    Да, если поменять на szOID_RSA_RC4 то все работает. Похоже, именно это мне и нужно в конце-концов:)
    Но все же интересно таки почему не получается предыдущий пример? Ведь я провайдер как раз для того самого ключа и для того самого алгоритма создаю, причем успешно. Должно же работать?...