OpenSSL의 ECC 지원을 사용하여 텍스트 문자열을 암호화하거나 해독하는 방법은 무엇입니까? OpenSSL API를 사용하여 ECC 개인/공개 키를 생성 할 수 있지만 이러한 키를 사용하여 일반 텍스트를 암호화하는 방법을 알지 못합니다.OpenSSL ECC를 사용하여 텍스트 문자열 암호화/해독
답변
ECC 자체는 타원 곡선을 기반으로하는 알고리즘이 실제로 암호화/암호 해독 작업을 정의하지 않습니다.
예를 들어 Elliptic-Curve Diffie-Hellman이 있습니다. 다음과 같이 ECDH를 사용하여 메시지를 암호화 할 수 있습니다.
- 임시 EC 키 생성.
- 해당 키와받는 사람의 공개 키를 사용하여 ECDH를 사용하여 암호를 생성하십시오.
- AES와 같은 대칭 암호로 메시지를 암호화하는 키로 해당 암호를 사용하십시오.
- 로드 메시지로부터 임시 공용 키
- 해독 단계 1.
에서 생성 된 암호화 된 메시지와 임시 공개 키를 전송한다.
- ECDH를 사용하여 비밀 키를 생성하려면 공개 키와 수신자 키를 함께 사용하십시오.
- 대칭 암호를 사용하여 메시지의 암호를 해독하는 데 해당 암호를 키로 사용합니다.
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 바이트 이상의 키 자료가 필요한 경우에는 더 긴 해시가 순서대로 처리됩니다.
수신자는 개인 키가 이미 존재하므로 (발신자가 해당 공개 키를 미리 알고 있어야하므로) 비슷한 것을 수행하고 공개 키가 발신자로부터 수신됩니다.
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 코드는 사용자가 직접 볼 수 있습니다).
작성한 언어는 무엇입니까? – starbeamrainbowlabs
- 1. openssl-2와 openssl-3의 차이점
- 2. CSS를 사용하여 긴 텍스트 문자열 서식 지정
- 3. openssl/valgrind
- 4. 쿼리 문자열 텍스트 암호화
- 5. OpenSSL - SSL_CTX_set_default_passwd_cb
- 6. iPhone에서 OpenSSL
- 7. 목록 - 문자열 - 텍스트 파일
- 8. Dozer지도 텍스트 문자열
- 9. aes-256-cbc를 사용하여 openssl hmac
- 10. openssl dgst를 사용하여 파일 서명 확인
- 11. OpenSSL API를 사용하여 Windows CryptoAPI CryptDeriveKey 구현
- 12. 텍스트 문자열 바꾸기
- 13. TypeError : 잘못된 인수 (문자열)! (예상되는 종류의 OpenSSL :: Digest :: Digest)
- 14. Android에서 OpenSSL 빌드하기 NDK
- 15. 문자열 조작, 텍스트 검색
- 16. 긴 텍스트 문자열 저장
- 17. ASCII 텍스트 문자열 단축
- 18. 전체 텍스트 문자열
- 19. 두 문자열 (텍스트 파일)
- 20. OpenSSL - AES 키 찾기
- 21. OpenSSL 서버 암호 선택
- 22. OPENSSL 질문 - 리눅스 .cpp 코드
- 23. OpenSSL 쿠키 지원
- 24. 동일한 매개 변수를 사용하여 텍스트를 암호화하는 경우 OpenSSL 명령 줄과 Ruby OpenSSL 라이브러리가 다릅니다
- 25. 문자열 목록에 텍스트 서식 지정
- 26. OpenSSL 라이브러리를 프로그램에 연결
- 27. C하려면 openssl 오류
- 28. openssl BF_cfb64_encrypt 스레드 안전성
- 29. 은 OpenSSL RAND_bytes 알고리즘
- 30. OpenSSL 코드 문제
caf, 답장을 보내 주셔서 감사합니다. 나는 위에서 언급 한 단계에서 문제가 없습니다. ECDH를 사용하여 생성 된 비밀을 사용하여 메시지를 암호화하기 위해 AES를 사용할 수 있습니다. 위의 단계를 수행하는 샘플 프로그램이 있습니까? 그렇다면 저를 지적하십시오. 나는 그런 샘플 프로그램을 찾기 위해 많은 노력을했지만 운이 없었습니다. 감사합니다 –
당신은 발신자/수신자 (가능하면 OpenSSL 사용) 사이의 비밀을 생성하는 방법에 대한 더 많은 참고 자료를 가리킬 수 있습니까? 고맙습니다. – ALOToverflow