Экспорт общего секрета в виде массива BYTE из BCRYPT_SECRET_HANDLE

Я реализую ECDHE, используя криптографические API следующего поколения (CNG). Я успешно генерирую открытый и закрытый ключи. Для предварительного общего ключа я использую BCryptSecretAgreement API, который возвращает мне секретный дескриптор общего ключа (BCRYPT_SECRET_HANDLE).

Как я могу экспортировать общий ключ в виде массива BYTE из BCRYPT_SECRET_HANDLE?


person Ahsan Younis    schedule 30.06.2016    source источник


Ответы (4)


Начиная с Windows 10, вы можете вызывать BCryptDeriveKey() с помощью BCRYPT_KDF_RAW_SECRET.

Полученные данные ключа являются необработанным секретом.

Примечание 1: bcrypt.h указывает, что этот формат работает для «WINBLUE», то есть для Windows 8.1, если я правильно понимаю, но я получил STATUS_NOT_SUPPORTED за использование этого типа KDF как в Windows 8.1, так и в Windows Server 2012 R2. Однако это работает в Windows 10.)

Примечание 2. Я обнаружил, что данные, возвращаемые с использованием этого типа KDF, имеют прямой порядок байтов (в то время как все остальное в BCrypt имеет обратный порядок байтов). Таким образом, чтобы использовать значение в мире с обратным порядком байтов, вам необходимо инвертировать данные по байтам.

person Scott Straw    schedule 30.05.2019
comment
не могли бы вы поделиться ссылкой на пример кода? Я смотрю на это, чтобы решить проблему с libssh2 github.com/libssh2/libssh2/issues/ 388, и это было бы очень полезно. У меня есть что-то, что доходит до этого финального этапа, а затем я успешно извлекаю буфер, полный нулей :-( - person Wez Furlong; 31.07.2019

Мне нужно было сделать следующее, и вот отрывок из моего кода, который выполняет критические элементы, вам нужно будет импортировать закрытый и открытый ключи перед этим сегментом.

DWORD bCryptStatus;
BCRYPT_SECRET_HANDLE secretHandle = NULL;
BCRYPT_KEY_HANDLE privateKeyHandle= NULL;
BCRYPT_KEY_HANDLE importedPublicKey = NULL;
BYTE *agreedSecret = NULL;
DWORD agreedSecretLength = 0;

//Import your keys here

//Generate the secret from the imported keys
bCryptStatus= BCryptSecretAgreement(privateKeyHandle, importedPublicKey, &secretHandle, 0);

//Now get the raw value of the secret agreement and copy it into an array
bCryptStatus= BCryptDeriveKey(
    secretHandle,          // Secret agreement handle
    BCRYPT_KDF_RAW_SECRET, // Key derivation function (null terminated unicode string)
    NULL,                  // KDF parameters
    NULL,                  // Buffer that recieves the derived key 
    0,                     // Length of the buffer
    &agreedSecretLength,   // Number of bytes copied to the buffer
    0);                    // Flags

    agreedSecret = (PBYTE)MALLOC(agreedSecretLength);

if (NULL != agreedSecret)
{
    _nCryptError = BCryptDeriveKey(
    secretHandle,          // Secret agreement handle
    BCRYPT_KDF_RAW_SECRET, // Key derivation function (null terminated unicode string)
    NULL,                  // KDF parameters
    agreedSecret,          // Buffer that recieves the derived key 
    agreedSecretLength,    // Length of the buffer
    &agreedSecretLength,   // Number of bytes copied to the buffer
    0);                    // Flags
}

//Free all the objects and the array when you are done, otherwise you will get memory leaks
if (NULL != importedPublicKey)
{
    BCryptDestroyKey(importedPublicKey);
}

if (NULL != privateKeyHandle)
{
    BCryptDestroyKey(privateKeyHandle);
}

if (NULL != secretHandle)
{
    BCryptDestroySecret(secretHandle);
}

if (NULL != agreedSecret)
{
    FREE(agreedSecret);
}

В качестве примечания: если вы используете NCrypt, это также будет работать (NCryptDeriveKey), я проверил это на своем производственном коде. Как было сказано ранее, массив будет перевернут, и вам нужно будет перевернуть массив байтов, чтобы получить секрет.

person Russell Gantman    schedule 14.10.2019

Получив BCRYPT_SECRET_HANDLE, вы используете BCryptDeriveKey для получения фактического симметричного ключа шифрования.

person Codeguard    schedule 12.11.2016
comment
На самом деле это не отвечает на исходный вопрос. Чего это не делает, так это извлечения общего ключа. Он получает что-то из общего секрета, но, похоже, нет никакого способа получить сам общий ключ. - person Tom Quarendon; 02.05.2017

После вызова BCryptSecretAgreement вам необходимо использовать BCryptDeriveKey для получения общего секрета.

Это можно сделать следующим образом:

// generates an ECDH shared secret from a public key and a private key
int get_ECDH_key(BCRYPT_KEY_HANDLE pubkey, BCRYPT_KEY_HANDLE privkey, unsigned char **key,
                 unsigned int *keylen)
{
    SECURITY_STATUS sstatus;
    BCRYPT_SECRET_HANDLE secret;
    int _len;

    // creates the shared secret, stored in a BCRYPT_SECRET_HANDLE 
    sstatus = BCryptSecretAgreement(privkey, pubkey, &secret, 0);
    if (!BCRYPT_SUCCESS(sstatus)) {
        printf("BCryptSecretAgreement failed with status %d", sstatus);
        return 0;
    }

    // find out how much space is needed before retrieving the shared secret
    sstatus = BCryptDeriveKey(secret, BCRYPT_KDF_HASH, NULL, NULL, 0, &_len, 0);
    if (!BCRYPT_SUCCESS(sstatus)) {
        printf("BCryptDeriveKey failed with status %d", sstatus);
        return 0;
    }

    // allocate space for the shared secret
    *key = malloc(_len);
    if (*key == NULL) {
        perror("malloc failed");
        exit(1);
    }

    // retrieve the shared secret
    sstatus = BCryptDeriveKey(secret, BCRYPT_KDF_HASH, NULL, *key, _len,
                              keylen, 0 );
    if (!BCRYPT_SUCCESS(sstatus)) {
        printf("BCryptDeriveKey failed with status %d", sstatus);
        return 0;
    }
    return 1;
}

Для второго параметра константа BCRYPT_KDF_HASH говорит об использовании хэша в качестве функции получения ключа. Хэш для использования может быть указан в третьем параметре. В этом примере третий параметр имеет значение NULL, поэтому по умолчанию используется SHA1.

Также четвертый параметр, который является указателем на буфер для получения ключа, может быть NULL. Если это так, ключ не копируется, однако количество копируемых байтов записывается по адресу, заданному шестым параметром. Это позволяет нам выделить нужное количество места, а затем снова вызвать функцию, на этот раз передав адрес выделенного буфера.

person dbush    schedule 13.11.2016
comment
На самом деле это не отвечает на исходный вопрос. Чего это не делает, так это извлечения общего ключа. Он получает что-то из общего секрета, но, похоже, нет никакого способа получить сам общий ключ. - person Tom Quarendon; 02.05.2017
comment
@TomQuarendon Этот код действительно извлекает общий секрет. После второго вызова BCryptDeriveKey в *key есть *keylen байта, которые представляют собой общий ключ. Обе стороны обмена будут иметь одинаковое значение, учитывая их собственный закрытый ключ и открытый ключ другой стороны. - person dbush; 02.05.2017
comment
Он выводит общий ключ a, да. Чего он не делает, например, в случае с Диффи Хеллманом, так это не дает вам ответа на g**xy mod p. В лучшем случае, если вы не укажете начало и конец, это даст вам хэш SHA1 этого значения. Поэтому, если вы пытаетесь написать что-то, совместимое, скажем, с реализацией DH на Java, и в соответствии со спецификацией, такой как, скажем, SSH, вам не повезло. Если вы не можете указать функцию вывода нулевого ключа, но пока мне это не удалось. - person Tom Quarendon; 02.05.2017
comment
Он не обещал генерировать то, что вы хотите, поэтому вы можете этого не делать. - person Codeguard; 02.05.2017
comment
@Codeguard: для меня API несовершенен, поскольку он не дает вам доступа к общему ключу. Он позволяет вам запускать на нем одну из небольшого набора функций вывода ключей, но вы не можете запускать пользовательскую функцию вывода ключей. Таким образом, он выполняет всю тяжелую криптографическую работу по обмену ключами DH или ECCDH, но не позволяет вам выполнять простую часть получения ключа по индивидуальному заказу, который мог бы получить необработанный доступ к общему ключу, например необходимо для соблюдения спецификации SSH. Я понимаю, что SSH не является широко используемым протоколом, но некоторым он может быть интересен. - person Tom Quarendon; 03.05.2017