Tistory View
명령줄에서 다음과 같이 암호화 작업을 했다면
openssl enc -e -aes-256-cbc -in plain.txt -out encrypted.data -k "my_password"
이렇게 생성된 파일을 복호화하는 코드를 작성해 보자.
실제 위의 명령줄은 문제가 많지만, 처음 openssl을 접하는 사용자는 위의 명령을 통해 암호화작업을 많이 하게 될 것이다. 그러다 보니, 이 문제많은 방식에서 다른 방식으로 변경할 경우 일단 복호화작업을 해야 하니, 복호화 프로그래밍도 익힐 겸 복호화하는 코드를 생성해보자.
저장된 파일의 구조는 이전 포스트에서도 언급했듯이 다음의 구조를 가진다.
검은색 부분에 "Salt__"(8bytes)가 있으며, 이어서 실제 Salt값(노란색)이 8bytes가 따라온다.
그 다음 빨간색부분이 암호화된 데이터이다.
일단 header파일을 불러온다.
#include <openssl/evp.h>
다음의 함수는 이 암호화된 파일의 내용(파일이 아니고)을 복호화하는 함수이다.
리턴값은 성공의 여부를 나타내고, 성공할 경우 decrypted에 복호화된 결과가 들어가게 된다.
bool decrypt_def_openssl_encoded_content( std::string* decrypted, const uint8_t* encrypted, int len, const char* szPassword )
{
bool ret = false;
uint8_t key[32];
uint8_t iv[16];
uint8_t salt[8];
int srcRemain;
uint8_t* pEnc;
uint8_t decBuf[1024+16]; // EVP_DecryptUpdate는 입력값보다 16byte[ciper-size] 더 큰 buffer를 넣어줘야한다.
// ciper size 는 -> EVP_CIPHER_block_size(EVP_aes_256_cbc())
int nread;
int outLen;
EVP_CIPHER_CTX *ctx = NULL;
// salt때문에 크기는 16바이트보다 클 수밖에 없다.
if( len < 16 ) {
fprintf( stderr, "file is too short < 16 bytes\n" );
return false;
}
// salt값을 빼온다.
// "Salt__"라는 signature를 체크하는 부분은 생략했다.
memcpy( salt, encrypted + 8, 8 );
// salt와 넘어온 비밀번호로 key와 iv를 추출한다.
// count 값으로 1을 넣었는 데, 이는 openssl명령이 이 값을 1로 처리하고 있어서다.
// 여기서는 md(message digest)로 sha256을 사용하지만, 구버전에서 만들어진 파일을 풀려면, md5를 넘겨야한다.
if( 0 == EVP_BytesToKey( EVP_aes_256_cbc(),EVP_sha256(), salt, (uint8_t*)szPassword, strlen(szPassword), 1, key, iv ) ) {
fprintf( stderr, "key iv deriving failure\n" );
goto last;
}
if(!(ctx = EVP_CIPHER_CTX_new())) {
fprintf( stderr, "create context failure" );
goto last;
}
EVP_DecryptInit_ex( ctx, EVP_aes_256_cbc(), NULL, key, iv );
// 암호화된 데이터를 밀어넣으면서, 복호화된 정보를 만든다.
srcRemain = len - 16; // 처음 salt부분은 포함하지 않는다.
pEnc = (uint8_t*)encrypted + 16; // 처음 salt부분은 포함하지 않는다
while( srcRemain > 0 )
{
nread = 1024;
if( srcRemain < nread ) {
nread = srcRemain;
}
if (1 != EVP_DecryptUpdate(ctx, decBuf, &outLen, pEnc, nread ) ) {
fprintf( stderr, "error while decrypting\n" );
goto last;
}
decrypted->append( (char*)decBuf, outLen );
pEnc += nread;
srcRemain -= nread;
}
// 암호화된 정보는 다 밀어 넣었으니, 남아있는 자투리 복호화데이터를 뽑는다.
// 이부분에서 비밀번호가 올바른지, 복호화가 잘 됐는지 체크할 수 밖에 없다.
if (1 != EVP_DecryptFinal_ex( ctx, decBuf, &outLen ) ) {
fprintf( stderr, "error while final can be invalid password, or corrupted\n" );
goto last;
}
decrypted->append( (char*)decBuf, outLen );
ret = true;
last:
if( ctx ) {
EVP_CIPHER_CTX_free( ctx );
}
return ret;
}
주의해야할 점은
1. 예제 코드에서도 언급 했듯이, EVP_DecryptUpdate를 사용할 경우 dec버퍼가 enc의 입력값보다 16byte가 더 커야 한다.(aes-256-cbc 일 경우16bytes)
2. 구버전의 경우 sha256이 아닌 md5로 해시를 지정해야 한다.
순서는
1. key와 iv를 만들고
2. cipher context를 초기화
3. 모든 암호화 데이터가 들어갈 때까지 EVP_DecryptUpdate
4. EVP_DecryptFinal_ex 로 마무리하면 된다.
다음의 함수는 위의 함수에 파일의 내용을 전달하기 위한 함수이다.
bool decrypt_def_openssl_encoded_file( std::string* decrypted, const char* szEncFileName, const char* szPassword ) {
bool ret = false;
uint8_t* encContent = NULL;
FILE* pf = NULL;
size_t fileSize;
pf = fopen( szEncFileName, "rb" );
if( !pf ) {
fprintf( stderr, "can't open file %s", szEncFileName );
goto last;
}
// 메모리 할당으 위해 파일의 크기를 구한다.
fseek( pf, 0, SEEK_END );
fileSize = ftell( pf );
encContent = (uint8_t*)malloc( fileSize );
// 원래 위치로 되돌리고
fseek( pf, 0, SEEK_SET );
// 몽땅 읽어버린다.
fread( encContent, 1, fileSize, pf );
ret = decrypt_def_openssl_encoded_content( decrypted, encContent, fileSize, szPassword );
last:
if( encContent ) {
free( encContent );
}
if( pf ) {
fclose(pf);
pf = NULL;
}
return ret;
}
그냥 몰빵으로 읽어서 전달하는 방식으로 만들었다.
만약 복호화할 파일이 크다면, 위 두개의 함수를 적절히 섞어서 변경하길 바란다.
C/C++ 코드다 보니 적절한 Library를 링크해줘야 하는 데,... (안그러면 undefined symbol 이 뜰 테니..)
이게 워낙에 다채로워서??
다음의 것들을 모두 시도해보기 바란다. 필자는 cygwin에서는 -lcrypto로 링크하였다.
-lssl
-lopenssl
-lcrypto
만약 -iter 나 -pbkdf2 를 지정하였다면 위의 EVP_BytesToKey함수 대신에 다음과 같이 적절히 교체해서 사용하면 된다.
int iter = 1; // 암호화시 지정한 횟수
uint8_t keyiv[32+16];
PKCS5_PBKDF2_HMAC( password, strlen(password), salt, sizeof(salt), iter, EVP_sha256(), sizeof(keyiv), keyiv );
uint8_t* key = keyiv;
uint8_t* iv = keyiv + 32;
기본 openssl명령은 PKCS5_PBKDF2_HMAC 함수를 이용해서 한번에 key와 iv를 만들어 나눠서 사용하고 있다.
(에궁.. 그리고보니 필자의 작업중에 잘못 만든 부분이 떠올랐다....아 그거 바꾸면, 10GiB데이터 다 바꿔야 하는데..ㅠㅠ)
'openssl' 카테고리의 다른 글
복호화(PHP) : aes256 cbc openssl (4) | 2020.03.23 |
---|---|
명령줄 : aes256 cbc openssl (2) | 2020.03.23 |
- Total
- Today
- Yesterday
- 예금
- 컴퓨트쉐이더
- 애드핏
- 안드로이드
- ComputeShader
- 경제보복
- 전기료
- 에어컨
- 금리
- 공유 컨텍스트
- 애드센스
- OpenGLes
- 전기요금
- OpenGL ES
- Android
- 에어콘
- 전기세
- texture
- 사용료
- 블로그
- TTS
- 적금
- 아끼는 법
- gpgpu
- 재태크
- choreographer
- 티스토리
- 텍스처
- 재테크
- 컴퓨트셰이더
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |