Ключевое слово в защите информации
ключевое слово
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

2 Страницы12>
Опции
К последнему сообщению К первому непрочитанному
Offline Андрей Куликов  
#1 Оставлено : 20 ноября 2010 г. 13:00:17(UTC)
Андрей Куликов

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 17.10.2010(UTC)
Сообщений: 147
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 2 раз
Поблагодарили: 10 раз в 9 постах
Вот код.
Если удалить контейнер и запустить его, то ф-я CryptEncrypt успешно выполняется.
Но если запустить его второй раз - она валится.

Код:


/**
 * Show various key properties.
 * @param hKey key to be described.
 */
void describeKey(HCRYPTKEY  hKey){

    ALG_ID algID;
    DWORD  keyLen;
    DWORD  keyPermissions;

    DWORD datalen = sizeof(algID);

    if(CryptGetKeyParam(hKey, KP_ALGID, &algID , &datalen, 0))
    {
        printf("ALG_ID: 0x%X\n", algID);
    }
    else{
        DBG_OUT_E("CryptGetKeyParam error");
    }
    
    datalen = sizeof(keyLen);
    if(CryptGetKeyParam(hKey, KP_KEYLEN, &keyLen, &datalen, 0))
    {
        printf("Key len: %d\n", keyLen);
    
    }
    else{
        DBG_OUT_E("CryptGetKeyParam error");
    }

    datalen = sizeof(keyPermissions);
    if(CryptGetKeyParam(hKey, KP_PERMISSIONS, &keyPermissions, &datalen, 0))
    {
        printf("Key permissions: 0x%X\n", keyPermissions);
    }
    else{
        DBG_OUT_E("CryptGetKeyParam error");
    }
}

/**
 * Converts raw key data to HCRYPTKEY suitable for later usage.
 * @param hProvider must be exportable
 * @param key raw key
 * @return  - HCRYPTKEY if success
 *          - 0 if fails
 */
HCRYPTKEY getCAPIKeyFromRawData(HCRYPTPROV hProvider, const unsigned char *key){

    HCRYPTKEY  hImpKey = 0;

    if (CryptGetUserKey(hProvider, AT_KEYEXCHANGE, &hImpKey))
    {
        DBG_OUT("Existing exch key used.");
        describeKey(hImpKey);
    }else{
        DBG_OUT_E("CryptGetUserKey failed (non-fatal)");
        if (GetLastError() != NTE_NO_KEY) {
            DBG_OUT_E("CryptGetUserKey returns not NTE_NO_KEY");
            goto done;
        }
        if (!CryptGenKey(hProvider, AT_KEYEXCHANGE, 0, &hImpKey)){
            DBG_OUT_E("CryptGenKey failed");
            goto done;
        }
        describeKey(hImpKey);
    }

    DWORD encKeyLength = 32; // 256 bits;
    // Calculate required length of data for the encrypted key.
    if (!CryptEncrypt(hImpKey, 0, TRUE, 0, 0, &encKeyLength, encKeyLength)){
        DBG_OUT_E("CryptEncrypt can't calculate required encrypted key size.");
        goto done;
    }

    printf("%s:%d:%s Calculated encrypted key length: %d\n", __FILE__, __LINE__, __FUNCTION__, encKeyLength);

  done:
    CryptDestroyKey(hImpKey);
    DBG_OUT_E("CryptDestroyKey returns:");


    return hKey;
}



Вот вывод первого запуска:
0x8009000D - это NTE_NO_KEY
Цитата:

MSG CAPIKeyFromRaw.c:129:main: -->> Start
MSG CAPIKeyFromRaw.c:152:main: CryptAcquireContext EMPTY failed : 0x80090019
MSG CAPIKeyFromRaw.c:162:main: New container created,
MSG CAPIKeyFromRaw.c:172:main: Pin was set.
MSG CAPIKeyFromRaw.c:80:getCAPIKeyFromRawData: CryptGetUserKey failed (non-fatal) : 0x8009000D
ALG_ID: 0xAA24
Key len: 512
Key permissions: 0x198
CAPIKeyFromRaw.c:113:getCAPIKeyFromRawData Calculated encrypted key length: 32
MSG CAPIKeyFromRaw.c:118:getCAPIKeyFromRawData: CryptDestroyKey returns: : 0x8009000D
MSG CAPIKeyFromRaw.c:192:main: <<-- Stop


Вот вывод второго запуска:
0x80090003 - это NTE_BAD_KEY
Цитата:

MSG CAPIKeyFromRaw.c:129:main: -->> Start
MSG CAPIKeyFromRaw.c:148:main: Existing temp keyset used
MSG CAPIKeyFromRaw.c:172:main: Pin was set.
MSG CAPIKeyFromRaw.c:77:getCAPIKeyFromRawData: Existing exch key used.
ALG_ID: 0xAA24
Key len: 512
Key permissions: 0x998
hImpKey = 0x9857253
MSG CAPIKeyFromRaw.c:109:getCAPIKeyFromRawData: CryptEncrypt can't calculate required encrypted key size. : 0x80090003
MSG CAPIKeyFromRaw.c:118:getCAPIKeyFromRawData: CryptDestroyKey returns: : 0x80090003
MSG CAPIKeyFromRaw.c:192:main: <<-- Stop


Разница у ключей - только в пермишнах.
0x0800 - это CRYPT_PREUSER_KEY
Больше вроде ничего не подходит...

Задание при генерации ключей флага CRYPT_EXPORTABLE приводит к точно таким же результам (естественно, пермишны меняются).

Что я делаю не так?
Как сделать так, чтобы возвращаемый CryptGetUserKey ключ был "хорош"?

Отредактировано пользователем 21 ноября 2010 г. 2:18:54(UTC)  | Причина: Не указана

Offline Максим Коллегин  
#2 Оставлено : 20 ноября 2010 г. 16:38:12(UTC)
Максим Коллегин

Статус: Сотрудник

Группы: Администраторы
Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,422
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 38 раз
Поблагодарили: 734 раз в 632 постах
Вроде и первый раз работать не должно на нашем криптопровайдере.
А что предполагается, сделает CryptEncrypt на ключе обмена?
Знания в базе знаний, поддержка в центре поддержки
Offline Андрей Куликов  
#3 Оставлено : 20 ноября 2010 г. 18:26:16(UTC)
Андрей Куликов

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 17.10.2010(UTC)
Сообщений: 147
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 2 раз
Поблагодарили: 10 раз в 9 постах
ОООооо...
Так оно и в первый раз работать не должно...
Значит ошибка всё-таки в ДНК...

В данном случае оно ничего на ключе обмена не делает - просто длинну результирующего шифртекста вычисляет.

Зачем вообще это нужно: мне надо зашифроввывать/расшифроввывать данные 28147 на ключе, который из вне в программу в явном виде поступает.
В виде байтиков.
Так как СryptImportKey принимает ключ в зашифрованом виде, я пытаюсь его зашифровать, чтобы потом скормить его этой функции.

Может быть есть решение исходной проблеммы с использоанием замечального криптопровайдера?

P.S. Linux ia32
Offline Максим Коллегин  
#4 Оставлено : 20 ноября 2010 г. 22:16:08(UTC)
Максим Коллегин

Статус: Сотрудник

Группы: Администраторы
Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,422
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 38 раз
Поблагодарили: 734 раз в 632 постах
ключ "в виде байтиков" импортировать в провайдер сложнее, чем написать примитивную реализацию ГОСТ-шифрования. Почитайте RFC 4357. Задача весьма странная - а зачем?
Знания в базе знаний, поддержка в центре поддержки
Offline Андрей Куликов  
#5 Оставлено : 21 ноября 2010 г. 2:15:15(UTC)
Андрей Куликов

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 17.10.2010(UTC)
Сообщений: 147
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 2 раз
Поблагодарили: 10 раз в 9 постах
Трудности мне не страшны.
Требования такие. Провайдер необходимо использовать.
В детали, к сожалению, пришлось бы долго вдаваться.

Рецепт импорта ключа из "сырых" данных взят отсюда:
http://etutorials.org/Pr...bject+from+Raw+Key+Data/

Могли бы Вы намекнуть на более верное направление с использованием замечательного криптопровайдера?
И почему оно и в первый раз не работает? (CryptDestroyKey возвращает NTE_NO_KEY) :(

Отредактировано пользователем 21 ноября 2010 г. 2:18:06(UTC)  | Причина: Не указана

Offline Максим Коллегин  
#6 Оставлено : 21 ноября 2010 г. 3:00:41(UTC)
Максим Коллегин

Статус: Сотрудник

Группы: Администраторы
Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,422
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 38 раз
Поблагодарили: 734 раз в 632 постах
Хм. Не вижу проверки кода возврата. GetLastError() не обязан быть 0 при успешном завершении, наоборот верно.
Про raw - я дал название RFC - разбирайтесь. Криптопровайдер в этом не поможет.
Задача - создать зашифрованный неким ключом симметричный ключ.
Знания в базе знаний, поддержка в центре поддержки
Offline Максим Коллегин  
#7 Оставлено : 21 ноября 2010 г. 3:05:06(UTC)
Максим Коллегин

Статус: Сотрудник

Группы: Администраторы
Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,422
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 38 раз
Поблагодарили: 734 раз в 632 постах
Хотя нет - вру. В статье идея верная, только стоит использовать временный сессионный ключ, а не ключ обмена (ГОСТ в отличие от RSA на ключе обмена шифровать не позволяет). Алгоритм шифрования тоже нужно подобрать будет.
Но конструировать блоб сесионного ключа придется вручную. Документация - в wincrypex.h и rfc 4357.

Отредактировано пользователем 21 ноября 2010 г. 3:05:54(UTC)  | Причина: Не указана

Знания в базе знаний, поддержка в центре поддержки
Offline Андрей Куликов  
#8 Оставлено : 21 ноября 2010 г. 4:32:58(UTC)
Андрей Куликов

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 17.10.2010(UTC)
Сообщений: 147
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 2 раз
Поблагодарили: 10 раз в 9 постах
Грандиозно!
Спасибо. Буду курить до просветления...

Тем более 28147 я для примера только привел.
Ключи 3410 тоже надо будет импортировать....
Offline Андрей Куликов  
#9 Оставлено : 30 ноября 2010 г. 16:39:55(UTC)
Андрей Куликов

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 17.10.2010(UTC)
Сообщений: 147
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 2 раз
Поблагодарили: 10 раз в 9 постах
Да, пока не завалило рутиной: докурился до успеха.

Правда совсем в соответствии с RFC 4357 не получилось - если UKM рандомным сделать - то ничtго не работает.
Но для сельской месности сойдет.

Код:

#define DBG_OUT(msg) fprintf(stderr, "MSG %s:%d:%s: %s\n", __FILE__, __LINE__, __FUNCTION__, msg);

#define DBG_OUT_E(msg) fprintf(stderr, "MSG %s:%d:%s: %s : 0x%X\n", __FILE__, __LINE__, __FUNCTION__, msg, GetLastError());


static int fill_default_simpleblob(CRYPT_SIMPLEBLOB *blob)
{
    if (blob == NULL) {
        return 0;
    }

    /*BLOBHEADER*/
    blob->tSimpleBlobHeader.BlobHeader.bType = SIMPLEBLOB;
    blob->tSimpleBlobHeader.BlobHeader.bVersion = BLOB_VERSION; // 0x20 current.
    blob->tSimpleBlobHeader.BlobHeader.reserved = 0x0;
    blob->tSimpleBlobHeader.BlobHeader.aiKeyAlg = CALG_G28147; // CALG_G28147_IMIT; 

    blob->tSimpleBlobHeader.Magic = G28147_MAGIC;
    blob->tSimpleBlobHeader.EncryptKeyAlgId = CALG_G28147;

    return 1;
}

/** Magic value for EncryptionParamSet.
 * Designated to represent DER-encoded ASN1-structure for GOST 28147 paramset.
 * Probably it's corresponds to id-GostR3410-2001-CryptoPro-XchA-ParamSet.
 */
static const BYTE default_params[] = { 0x30, 0x09, 0x06, 0x07,
                                       0x2A, 0x85, 0x03, 0x02,
                                       0x02, 0x1F, 0x01 };

/**
 * Converts raw key data to HCRYPTKEY suitable for later usage.
 * @param hProvider must be exportable
 * @param key raw key (rfc4357 6.1 CEK)
 * @return  - HCRYPTKEY if success
 *          - 0 if fails
 */
HCRYPTKEY get_CAPI_key_from_raw(HCRYPTPROV hProvider, const unsigned char *key){

    /// The idea is from here:  http://etutorials.org/Programming/secure+programming/Chapter+5.+Symmetric+Encryption/5.26+Creating+a+CryptoAPI+Key+Object+from+Raw+Key+Data/
    /// But GOST has a specific...
    HCRYPTKEY  resKey  = 0;
    DWORD      dataLen = 0;

    // We decrement it by 4 due to 4 bytes alignment of BYTE bEncryptionParamSet[1] array.
    // @bug incompatible with x64?
    const size_t REAL_CRYPT_SIMPLEBLOB_LEN = sizeof(CRYPT_SIMPLEBLOB)
                                           + sizeof(default_params) - 4;

    CRYPT_SIMPLEBLOB *keyBlob = NULL;

    keyBlob = malloc(REAL_CRYPT_SIMPLEBLOB_LEN);
    if(!keyBlob){
        DBG_OUT_E("Can't allocate CRYPT_SIMPLEBLOB");
        goto done;
    }

    memset(keyBlob, 0 , REAL_CRYPT_SIMPLEBLOB_LEN);

    fill_default_simpleblob(keyBlob);
    
    // key is a CEK rfc4357 6.1
    memcpy(keyBlob->bEncryptedKey, key, G28147_KEYLEN);
    memcpy(keyBlob->bEncryptionParamSet, default_params, sizeof(default_params));


    // Generate random KEK rfc4357 6.1.1.
    HCRYPTKEY kek;
    if (!CryptGenKey(hProvider, CALG_G28147, CRYPT_EXPORTABLE, &kek))
    {
        DBG_OUT_E("Can't generate KEK");
        goto done;
    }


    DWORD dparam = 0;

    dparam = ZERO_PADDING;

    if (!CryptSetKeyParam(kek, KP_PADDING, (BYTE*)&dparam, 0))
    {
        DBG_OUT_E("Can't set ZERO_PADDING for generated KEK.");
        goto done;
    }

 /*  Well, let's assume UMK is always zeros...
    // Generate UKM rfc4357 6.1.1.
    if(!CryptGenRandom(hProvider, sizeof(keyBlob->bSV), keyBlob->bSV)){
      DBG_OUT_E("Can't generate UKM");
      goto done;
    }

 */
    // Compute CEK_MAC rfc4357 6.1.2.
    HCRYPTKEY im_key;
    if(!CryptDuplicateKey(kek,    // from
                         NULL, 0,  // two reserved
                         &im_key)) // to
    {
      DBG_OUT_E("Can't duplicate key for IMITO");
      goto done;
    }

    // Useless as UKV is always zeros, but still...
    // Set IV for IMITO
    if (!CryptSetKeyParam(
        im_key,
        KP_IV,
        keyBlob->bSV,
        0)) {
        DBG_OUT_E("Can't set IV for IMITO");
        goto done;
    }

    HCRYPTHASH im = 0;
    if(!CryptCreateHash(hProv, CALG_G28147_IMIT, im_key, 0, &im))
    {
      DBG_OUT_E("Can't create IMITO hash");
      goto done;
    }
 
    if(!CryptHashData(im, keyBlob->bEncryptedKey, G28147_KEYLEN, 0))
    {
        DBG_OUT_E("Can't hash CEK");
        goto done;
    }

    DWORD imitLen = EXPORT_IMIT_SIZE;

    if(!CryptGetHashParam(im,
                         HP_HASHVAL,
                         keyBlob->bMacKey, // This is a CEK_MAC in RFC4357 6.1.2
                         &imitLen, 0))
    {
        DBG_OUT_E("Can't get CEK hash");
        goto done;
    }
    // END compute CEK_MAC


    // Encrypt key to CEK_ENC rfc4357 6.1.3.
    HCRYPTKEY enc_key;

    if (!CryptDuplicateKey(kek, 0, 0, &enc_key)) {
        DBG_OUT_E("Can't dup KEK for encryption.");
        goto done;
    }

    dparam = CRYPT_MODE_ECB;
    if (!CryptSetKeyParam(enc_key, KP_MODE, (BYTE*)&dparam, 0)){
        DBG_OUT_E("Can't set ECB mode");
        goto done;
    }

    dataLen = G28147_KEYLEN;
    if (!CryptEncrypt(enc_key, 0, TRUE, 0, NULL, &dataLen, 0)) {
        DBG_OUT_E("Can't detemine required buffer size.");
        goto done;
    }

    printf("Buffer size required: %d\n", dataLen);

    if(G28147_KEYLEN != dataLen){
        DBG_OUT("Required buffer size and G28147_KEYLEN mismatch");
        goto done;
    }

    if (!CryptEncrypt(enc_key, 0, TRUE, 0, keyBlob->bEncryptedKey, &dataLen, dataLen)) {
        DBG_OUT_E("Can't encrypt raw key");
        goto done;
    }
    // END Encrypt key to CEK_ENC rfc4357 6.1.3.
    
    dparam = CALG_SIMPLE_EXPORT;
    if (!CryptSetKeyParam(kek, KP_ALGID, (BYTE *) & dparam, 0))
    {
        DBG_OUT_E("Can't set SIMPLE_EXPORT param for KEK");
        goto done;
    }

    dataLen = REAL_CRYPT_SIMPLEBLOB_LEN;
    if (!CryptImportKey(hProvider,
                      (BYTE*)keyBlob,
                      dataLen,
                      kek,
                      0,
                      &resKey))
    {
        DBG_OUT_E("Can't import key");
        resKey = 0;
        goto done;
    }else{
        DBG_OUT("Our key imported!");
    }


  done:

    if(!CryptDestroyKey(enc_key)){
        DBG_OUT_E("Fail to destroy enc_key:");
    }

    if(!CryptDestroyKey(enc_key)){
        DBG_OUT_E("Fail to destroy im_key:");
    }

    if(!CryptDestroyKey(enc_key)){
        DBG_OUT_E("Fail to destroy kek:");
    }

    free(keyBlob);

    return resKey;
}

Отредактировано пользователем 30 ноября 2010 г. 16:55:15(UTC)  | Причина: Не указана

thanks 1 пользователь поблагодарил Андрей Куликов за этот пост.
rik оставлено 30.07.2014(UTC)
Offline Максим Коллегин  
#10 Оставлено : 30 ноября 2010 г. 16:49:04(UTC)
Максим Коллегин

Статус: Сотрудник

Группы: Администраторы
Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,422
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 38 раз
Поблагодарили: 734 раз в 632 постах
Сильно!
Знания в базе знаний, поддержка в центре поддержки
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Guest
2 Страницы12>
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.