Шифрование/дешифрование текстовых строк с помощью OpenSSL ECC

вот Ваш пример в "одной" строке.

this.$OuterDiv = $('
') .hide() .append($('
') .attr({ cellSpacing : 0 }) .addClass("text") ) ;

Обновление : Я думал, что обновлю это сообщение, так как это все еще получает довольно мало трафика. В комментариях ниже существует некоторое обсуждение [приблизительно 114] по сравнению с $("

") по сравнению с $(document.createElement('div')) как способ создать новые элементы, и который является "лучшим".

я соединил маленький сравнительный тест , и здесь являюсь примерно результатами повторения вышеупомянутых опций 100,000 раз:

jQuery 1.4, 1.5, 1.6

               Chrome 11  Firefox 4   IE9
440ms 640ms 460ms
420ms 650ms 480ms createElement 100ms 180ms 300ms

jQuery 1.3

                Chrome 11
770ms
3800ms createElement 100ms

jQuery 1.2

                Chrome 11
3500ms
3500ms createElement 100ms

я думаю, что это не большое удивление, но document.createElement самый быстрый метод. Конечно, прежде чем Вы уходите и начинаете осуществлять рефакторинг свою всю кодовую базу, помните, что различия, о которых мы говорим здесь (во всех кроме архаичных версий jQuery) приравниваются приблизительно к дополнительным 3 миллисекундам на тысячу элементов .

Обновление 2

Обновленный для jQuery 1.7.2 и помещенный сравнительный тест на JSBen.ch, который является, вероятно, более научным, чем мои примитивные сравнительные тесты, плюс он может быть crowdsourced теперь!

http://jsben.ch/#/ARUtz

19
задан Pops 12 January 2012 в 21:22
поделиться

2 ответа

Сам по себе ECC на самом деле не определяет никаких операций шифрования / дешифрования - это делают алгоритмы, построенные на эллиптических кривых.

Одним из примеров является эллиптическая кривая Диффи-Хеллмана. Вы можете зашифровать сообщение с помощью ECDH:

  1. Сгенерировав эфемерный ключ EC.
  2. Используя этот ключ и открытый ключ получателя, сгенерируйте секрет с помощью ECDH.
  3. Используйте этот секрет как ключ для шифрования сообщение с симметричным шифром, например AES.
  4. Передайте зашифрованное сообщение и эфемерный открытый ключ, сгенерированный на шаге 1.

Чтобы расшифровать:

  1. Загрузите эфемерный открытый ключ из сообщения.
  2. Используйте этот открытый ключ вместе с вашим ключом получателя, чтобы сгенерировать секрет с помощью ECDH.
  3. Используйте этот секрет в качестве ключа для дешифрования сообщения с помощью симметричного шифра.

РЕДАКТИРОВАТЬ: Ниже приводится основная идея для создания секрета с использованием ECDH. Сначала нам нужно определить функцию получения ключа - в ней используется хэш SHA1.

void *KDF1_SHA1(const void *in, size_t inlen, void *out, size_t *outlen)
{
    if (*outlen < SHA_DIGEST_LENGTH)
        return NULL;
    else
        *outlen = SHA_DIGEST_LENGTH;
    return SHA1(in, inlen, out);
}

Это код ECDH для стороны отправителя. Предполагается, что открытый ключ получателя уже находится в «recip_key», и вы проверили его с помощью EC_KEY_check_key (). В нем также для краткости отсутствует очень важная проверка ошибок, которую вы определенно захотите включить в производственный код.

EC_KEY *ephemeral_key = NULL;
const EC_GROUP *group = NULL;
unsigned char buf[SHA_DIGEST_LENGTH] = { 0 };

group = EC_KEY_get0_group(recip_key);
ephemeral_key = EC_KEY_new();
EC_KEY_set_group(ephemeral_key, group);

EC_KEY_generate_key(ephemeral_key);
ECDH_compute_key(buf, sizeof buf, EC_KEY_get0_public_key(recip_key), ephemeral_key, KDF1_SHA1);

После этого буфер ' buf 'содержит 20 байтов материала, который вы можете использовать для ввода. Этот сокращенный пример основан на коде в "ecdhtest.c" в дистрибутиве с исходным кодом openssl - я предлагаю взглянуть на него.

Вы захотите отправить часть открытого ключа ephemeral_key с зашифрованным сообщением и безопасно удалить часть закрытого ключа. MAC для данных также является хорошей идеей, и если вам нужно более 20 байтов ключевого материала, возможно, потребуется более длинный хэш.

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

в дистрибутиве openssl с исходным кодом - я предлагаю взглянуть на него.

Вы захотите отправить часть открытого ключа ephemeral_key с зашифрованным сообщением и безопасно отказаться от части закрытого ключа. MAC для данных также является хорошей идеей, и если вам нужно более 20 байтов ключевого материала, возможно, потребуется более длинный хэш.

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

в дистрибутиве openssl с исходным кодом - я предлагаю взглянуть на него.

Вы захотите отправить часть открытого ключа ephemeral_key с зашифрованным сообщением и безопасно отказаться от части закрытого ключа. MAC для данных также является хорошей идеей, и если вам нужно более 20 байтов ключевого материала, возможно, потребуется более длинный хэш.

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

10
ответ дан 30 November 2019 в 04:20
поделиться

Поскольку так трудно найти примеры, показывающие, как использовать ECC для шифрования данных, я решил опубликовать некоторый код для других. Полный список смотрите в моем сообщении на openssl-dev:

http://www.mail-archive.com/openssl-dev@openssl.org/msg28042.html

В основном это готовая к использованию версия того, как использовать ECDH для защиты блока данных. ECDH используется для генерации общего секрета. Затем общий секрет хэшируется с помощью SHA 512. Полученные 512 бит делятся на части, 256 из которых служат ключом для симметричного шифра (AES 256 в моем примере), а остальные 256 бит используются в качестве ключа для HMAC. Моя реализация в значительной степени основана на стандарте ECIES, разработанном рабочей группой SECG.

Ключевыми функциями являются ecies_encrypt(), которая принимает открытый ключ в шестнадцатеричной форме и возвращает зашифрованные данные:

secure_t * ecies_encrypt(char *key, unsigned char *data, size_t length) {

void *body;
HMAC_CTX hmac;
int body_length;
secure_t *cryptex;
EVP_CIPHER_CTX cipher;
unsigned int mac_length;
EC_KEY *user, *ephemeral;
size_t envelope_length, block_length, key_length;
unsigned char envelope_key[SHA512_DIGEST_LENGTH], iv[EVP_MAX_IV_LENGTH], block[EVP_MAX_BLOCK_LENGTH];

// Simple sanity check.
if (!key || !data || !length) {
    printf("Invalid parameters passed in.\n");
    return NULL;
}

// Make sure we are generating enough key material for the symmetric ciphers.
if ((key_length = EVP_CIPHER_key_length(ECIES_CIPHER)) * 2 > SHA512_DIGEST_LENGTH) {
    printf("The key derivation method will not produce enough envelope key material for the chosen ciphers. {envelope = %i / required = %zu}", SHA512_DIGEST_LENGTH / 8,
            (key_length * 2) / 8);
    return NULL;
}

// Convert the user's public key from hex into a full EC_KEY structure.
if (!(user = ecies_key_create_public_hex(key))) {
    printf("Invalid public key provided.\n");
    return NULL;
}

// Create the ephemeral key used specifically for this block of data.
else if (!(ephemeral = ecies_key_create())) {
    printf("An error occurred while trying to generate the ephemeral key.\n");
    EC_KEY_free(user);
    return NULL;
}

// Use the intersection of the provided keys to generate the envelope data used by the ciphers below. The ecies_key_derivation() function uses
// SHA 512 to ensure we have a sufficient amount of envelope key material and that the material created is sufficiently secure.
else if (ECDH_compute_key(envelope_key, SHA512_DIGEST_LENGTH, EC_KEY_get0_public_key(user), ephemeral, ecies_key_derivation) != SHA512_DIGEST_LENGTH) {
    printf("An error occurred while trying to compute the envelope key. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
    EC_KEY_free(ephemeral);
    EC_KEY_free(user);
    return NULL;
}

// Determine the envelope and block lengths so we can allocate a buffer for the result.
else if ((block_length = EVP_CIPHER_block_size(ECIES_CIPHER)) == 0 || block_length > EVP_MAX_BLOCK_LENGTH || (envelope_length = EC_POINT_point2oct(EC_KEY_get0_group(
        ephemeral), EC_KEY_get0_public_key(ephemeral), POINT_CONVERSION_COMPRESSED, NULL, 0, NULL)) == 0) {
    printf("Invalid block or envelope length. {block = %zu / envelope = %zu}\n", block_length, envelope_length);
    EC_KEY_free(ephemeral);
    EC_KEY_free(user);
    return NULL;
}

// We use a conditional to pad the length if the input buffer is not evenly divisible by the block size.
else if (!(cryptex = secure_alloc(envelope_length, EVP_MD_size(ECIES_HASHER), length, length + (length % block_length ? (block_length - (length % block_length)) : 0)))) {
    printf("Unable to allocate a secure_t buffer to hold the encrypted result.\n");
    EC_KEY_free(ephemeral);
    EC_KEY_free(user);
    return NULL;
}

// Store the public key portion of the ephemeral key.
else if (EC_POINT_point2oct(EC_KEY_get0_group(ephemeral), EC_KEY_get0_public_key(ephemeral), POINT_CONVERSION_COMPRESSED, secure_key_data(cryptex), envelope_length,
        NULL) != envelope_length) {
    printf("An error occurred while trying to record the public portion of the envelope key. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
    EC_KEY_free(ephemeral);
    EC_KEY_free(user);
    secure_free(cryptex);
    return NULL;
}

// The envelope key has been stored so we no longer need to keep the keys around.
EC_KEY_free(ephemeral);
EC_KEY_free(user);

// For now we use an empty initialization vector.
memset(iv, 0, EVP_MAX_IV_LENGTH);

// Setup the cipher context, the body length, and store a pointer to the body buffer location.
EVP_CIPHER_CTX_init(&cipher);
body = secure_body_data(cryptex);
body_length = secure_body_length(cryptex);

// Initialize the cipher with the envelope key.
if (EVP_EncryptInit_ex(&cipher, ECIES_CIPHER, NULL, envelope_key, iv) != 1 || EVP_CIPHER_CTX_set_padding(&cipher, 0) != 1 || EVP_EncryptUpdate(&cipher, body,
        &body_length, data, length - (length % block_length)) != 1) {
    printf("An error occurred while trying to secure the data using the chosen symmetric cipher. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
    EVP_CIPHER_CTX_cleanup(&cipher);
    secure_free(cryptex);
    return NULL;
}

// Check whether all of the data was encrypted. If they don't match up, we either have a partial block remaining, or an error occurred.
else if (body_length != length) {

    // Make sure all that remains is a partial block, and their wasn't an error.
    if (length - body_length >= block_length) {
        printf("Unable to secure the data using the chosen symmetric cipher. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
        EVP_CIPHER_CTX_cleanup(&cipher);
        secure_free(cryptex);
        return NULL;
    }

    // Copy the remaining data into our partial block buffer. The memset() call ensures any extra bytes will be zero'ed out.
    memset(block, 0, EVP_MAX_BLOCK_LENGTH);
    memcpy(block, data + body_length, length - body_length);

    // Advance the body pointer to the location of the remaining space, and calculate just how much room is still available.
    body += body_length;
    if ((body_length = secure_body_length(cryptex) - body_length) < 0) {
        printf("The symmetric cipher overflowed!\n");
        EVP_CIPHER_CTX_cleanup(&cipher);
        secure_free(cryptex);
        return NULL;
    }

    // Pass the final partially filled data block into the cipher as a complete block. The padding will be removed during the decryption process.
    else if (EVP_EncryptUpdate(&cipher, body, &body_length, block, block_length) != 1) {
        printf("Unable to secure the data using the chosen symmetric cipher. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
        EVP_CIPHER_CTX_cleanup(&cipher);
        secure_free(cryptex);
        return NULL;
    }
}

// Advance the pointer, then use pointer arithmetic to calculate how much of the body buffer has been used. The complex logic is needed so that we get
// the correct status regardless of whether there was a partial data block.
body += body_length;
if ((body_length = secure_body_length(cryptex) - (body - secure_body_data(cryptex))) < 0) {
    printf("The symmetric cipher overflowed!\n");
    EVP_CIPHER_CTX_cleanup(&cipher);
    secure_free(cryptex);
    return NULL;
}

else if (EVP_EncryptFinal_ex(&cipher, body, &body_length) != 1) {
    printf("Unable to secure the data using the chosen symmetric cipher. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
    EVP_CIPHER_CTX_cleanup(&cipher);
    secure_free(cryptex);
    return NULL;
}

EVP_CIPHER_CTX_cleanup(&cipher);

// Generate an authenticated hash which can be used to validate the data during decryption.
HMAC_CTX_init(&hmac);
mac_length = secure_mac_length(cryptex);

// At the moment we are generating the hash using encrypted data. At some point we may want to validate the original text instead.
if (HMAC_Init_ex(&hmac, envelope_key + key_length, key_length, ECIES_HASHER, NULL) != 1 || HMAC_Update(&hmac, secure_body_data(cryptex), secure_body_length(cryptex))
        != 1 || HMAC_Final(&hmac, secure_mac_data(cryptex), &mac_length) != 1) {
    printf("Unable to generate a data authentication code. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
    HMAC_CTX_cleanup(&hmac);
    secure_free(cryptex);
    return NULL;
}

HMAC_CTX_cleanup(&hmac);

return cryptex;
}

И ecies_decrypt(), которая принимает закрытый ключ, опять же в шестнадцатеричной форме, и расшифровывает ранее защищенный буфер:

unsigned char * ecies_decrypt(char *key, secure_t *cryptex, size_t *length) {

HMAC_CTX hmac;
size_t key_length;
int output_length;
EVP_CIPHER_CTX cipher;
EC_KEY *user, *ephemeral;
unsigned int mac_length = EVP_MAX_MD_SIZE;
unsigned char envelope_key[SHA512_DIGEST_LENGTH], iv[EVP_MAX_IV_LENGTH], md[EVP_MAX_MD_SIZE], *block, *output;

// Simple sanity check.
if (!key || !cryptex || !length) {
    printf("Invalid parameters passed in.\n");
    return NULL;
}

// Make sure we are generating enough key material for the symmetric ciphers.
else if ((key_length = EVP_CIPHER_key_length(ECIES_CIPHER)) * 2 > SHA512_DIGEST_LENGTH) {
    printf("The key derivation method will not produce enough envelope key material for the chosen ciphers. {envelope = %i / required = %zu}", SHA512_DIGEST_LENGTH / 8,
            (key_length * 2) / 8);
    return NULL;
}

// Convert the user's public key from hex into a full EC_KEY structure.
else if (!(user = ecies_key_create_private_hex(key))) {
    printf("Invalid private key provided.\n");
    return NULL;
}

// Create the ephemeral key used specifically for this block of data.
else if (!(ephemeral = ecies_key_create_public_octets(secure_key_data(cryptex), secure_key_length(cryptex)))) {
    printf("An error occurred while trying to recreate the ephemeral key.\n");
    EC_KEY_free(user);
    return NULL;
}

// Use the intersection of the provided keys to generate the envelope data used by the ciphers below. The ecies_key_derivation() function uses
// SHA 512 to ensure we have a sufficient amount of envelope key material and that the material created is sufficiently secure.
else if (ECDH_compute_key(envelope_key, SHA512_DIGEST_LENGTH, EC_KEY_get0_public_key(ephemeral), user, ecies_key_derivation) != SHA512_DIGEST_LENGTH) {
    printf("An error occurred while trying to compute the envelope key. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
    EC_KEY_free(ephemeral);
    EC_KEY_free(user);
    return NULL;
}

// The envelope key material has been extracted, so we no longer need the user and ephemeral keys.
EC_KEY_free(ephemeral);
EC_KEY_free(user);

// Use the authenticated hash of the ciphered data to ensure it was not modified after being encrypted.
HMAC_CTX_init(&hmac);

// At the moment we are generating the hash using encrypted data. At some point we may want to validate the original text instead.
if (HMAC_Init_ex(&hmac, envelope_key + key_length, key_length, ECIES_HASHER, NULL) != 1 || HMAC_Update(&hmac, secure_body_data(cryptex), secure_body_length(cryptex))
        != 1 || HMAC_Final(&hmac, md, &mac_length) != 1) {
    printf("Unable to generate the authentication code needed for validation. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
    HMAC_CTX_cleanup(&hmac);
    return NULL;
}

HMAC_CTX_cleanup(&hmac);

// We can use the generated hash to ensure the encrypted data was not altered after being encrypted.
if (mac_length != secure_mac_length(cryptex) || memcmp(md, secure_mac_data(cryptex), mac_length)) {
    printf("The authentication code was invalid! The ciphered data has been corrupted!\n");
    return NULL;
}

// Create a buffer to hold the result.
output_length = secure_body_length(cryptex);
if (!(block = output = malloc(output_length + 1))) {
    printf("An error occurred while trying to allocate memory for the decrypted data.\n");
    return NULL;
}

// For now we use an empty initialization vector. We also clear out the result buffer just to be on the safe side.
memset(iv, 0, EVP_MAX_IV_LENGTH);
memset(output, 0, output_length + 1);

EVP_CIPHER_CTX_init(&cipher);

// Decrypt the data using the chosen symmetric cipher.
if (EVP_DecryptInit_ex(&cipher, ECIES_CIPHER, NULL, envelope_key, iv) != 1 || EVP_CIPHER_CTX_set_padding(&cipher, 0) != 1 || EVP_DecryptUpdate(&cipher, block,
        &output_length, secure_body_data(cryptex), secure_body_length(cryptex)) != 1) {
    printf("Unable to decrypt the data using the chosen symmetric cipher. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
    EVP_CIPHER_CTX_cleanup(&cipher);
    free(output);
    return NULL;
}

block += output_length;
if ((output_length = secure_body_length(cryptex) - output_length) != 0) {
    printf("The symmetric cipher failed to properly decrypt the correct amount of data!\n");
    EVP_CIPHER_CTX_cleanup(&cipher);
    free(output);
    return NULL;
}

if (EVP_DecryptFinal_ex(&cipher, block, &output_length) != 1) {
    printf("Unable to decrypt the data using the chosen symmetric cipher. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
    EVP_CIPHER_CTX_cleanup(&cipher);
    free(output);
    return NULL;
}

EVP_CIPHER_CTX_cleanup(&cipher);

*length = secure_orig_length(cryptex);
return output;
}

Я размещаю эту статью, потому что лично я не смог найти других примеров защиты файлов с помощью ECC и библиотеки OpenSSL. При этом стоит упомянуть альтернативные варианты, не использующие OpenSSL. Одним из них является seccure, который работает по схеме, аналогичной моему примеру, только в нем используется libgcrypt. Поскольку libgcrypt не предоставляет всех необходимых функций ECC, программа seccure заполняет пробелы и реализует логику ECC, отсутствующую в libgcrypt.

Еще одна программа, на которую стоит обратить внимание, - SKS, которая использует процесс шифрования на основе ECC, аналогичный приведенному выше примеру, но не имеет никаких внешних зависимостей (поэтому весь код ECC находится прямо здесь, чтобы вы могли посмотреть на него).

17
ответ дан 30 November 2019 в 04:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: