2009-07-20 4 views

답변

9

ECC 자체는 타원 곡선을 기반으로하는 알고리즘이 실제로 암호화/암호 해독 작업을 정의하지 않습니다.

예를 들어 Elliptic-Curve Diffie-Hellman이 있습니다. 다음과 같이 ECDH를 사용하여 메시지를 암호화 할 수 있습니다.

  1. 임시 EC 키 생성.
  2. 해당 키와받는 사람의 공개 키를 사용하여 ECDH를 사용하여 암호를 생성하십시오.
  3. AES와 같은 대칭 암호로 메시지를 암호화하는 키로 해당 암호를 사용하십시오.

    1. 로드 메시지로부터 임시 공용 키
    2. 해독 단계 1.

    에서 생성 된 암호화 된 메시지와 임시 공개 키를 전송한다.

  4. ECDH를 사용하여 비밀 키를 생성하려면 공개 키와 수신자 키를 함께 사용하십시오.
  5. 대칭 암호를 사용하여 메시지의 암호를 해독하는 데 해당 암호를 키로 사용합니다.

EDIT : 다음은 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 바이트의 재료가 포함되어 있습니다. 키잉에 사용할 수 있습니다. 이 축약 된 예제는 openssl 소스 배포판의 "ecdhtest.c"에있는 코드를 기반으로합니다.

ephemeral_key의 공개 키 부분을 암호화 된 메시지와 함께 보내고 비공개 키 부분을 안전하게 버리고 싶을 것입니다. 데이터에 대한 MAC도 좋은 생각이며, 20 바이트 이상의 키 자료가 필요한 경우에는 더 긴 해시가 순서대로 처리됩니다.

수신자는 개인 키가 이미 존재하므로 (발신자가 해당 공개 키를 미리 알고 있어야하므로) 비슷한 것을 수행하고 공개 키가 발신자로부터 수신됩니다.

+0

caf, 답장을 보내 주셔서 감사합니다. 나는 위에서 언급 한 단계에서 문제가 없습니다. ECDH를 사용하여 생성 된 비밀을 사용하여 메시지를 암호화하기 위해 AES를 사용할 수 있습니다. 위의 단계를 수행하는 샘플 프로그램이 있습니까? 그렇다면 저를 지적하십시오. 나는 그런 샘플 프로그램을 찾기 위해 많은 노력을했지만 운이 없었습니다. 감사합니다 –

+0

당신은 발신자/수신자 (가능하면 OpenSSL 사용) 사이의 비밀을 생성하는 방법에 대한 더 많은 참고 자료를 가리킬 수 있습니까? 고맙습니다. – ALOToverflow

16

ECC를 사용하여 데이터를 암호화하는 방법을 보여주는 예제를 찾기가 어려워서 다른 사람들이 사용할 수있는 코드를 게시 할 것이라고 생각했습니다.전체 목록은 내하려면 openssl-dev에 게시물을 체크 아웃 :

http://www.mail-archive.com/[email protected]/msg28042.html

는 기본적으로 ECDH를 사용하는 방법에 자사의 플러시 가능한 버전은 데이터 블록을 확보 할 수 있습니다. ECDH는 공유 비밀을 생성하는 데 사용됩니다. 그런 다음 공유 된 비밀은 SHA 512를 사용하여 해시됩니다. 결과 512 비트가 나뉘어져 256이 대칭 암호 (예 : AES 256)의 키 역할을하고 다른 256 비트는 HMAC의 키로 사용됩니다. 저의 구현은 SECG 워킹 그룹이 제시 한 ECIES 표준을 기반으로합니다.

의 핵심 기능은 육각 형태로 공개 키를 허용하고 암호화 된 데이터 반환) (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() 육각 형태로 다시 개인 키를 소요하고 해독 이전에 확보 버퍼 : 저는 개인적으로 ECC과에는 OpenSSL 라이브러리를 사용하여 파일을 보호하는 방법의 다른 예를 찾을 수 있기 때문에

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; 
} 

나는이 게시하도록하겠습니다. OpenSSL을 사용하지 않는 대안을 언급 할 가치가 있다고 말했습니다. 하나는 내 예제와 비슷한 패턴을 따르는 보안이다. 단지 libgcrypt에 의존한다. libgcrypt는 필요한 기본 ECC 기능을 모두 제공하지 않으므로 보안 프로그램은 libgcrypt에서 누락 된 ECC 논리를 채우고 ECC 논리를 구현합니다.

가치가있는 또 다른 프로그램은 위의 예와 비슷한 ECC 기반 암호화 프로세스를 사용하지만 외부 종속성을 갖지 않는 SKS입니다 (따라서 모든 ECC 코드는 사용자가 직접 볼 수 있습니다).

+0

작성한 언어는 무엇입니까? – starbeamrainbowlabs