본문 바로가기

Tech

[OpenSSL] EVP 방식으로 OpenSSL 사용하기 1/2

OpenSSL에서는 인터페이스가 규격화 된 최상위 API를 제공한다.

처음 접하는 사람들은 쉽게 접근할 수 있도록 배려한 것 같다.

 

참고로 EVP란 EnVeloPe 의 약자로 하위의 api를 포장했다는 의미가 있다.

 

 

예제를 보며 설명하겠다.

 

void TestEVP_MessageDigest(const char *alg, const char *pP1, const char *pP2, const char *pP3)
{
	EVP_MD_CTX *mctx;
	const EVP_MD *digest;
	digest = EVP_get_digestbyname(alg);
	unsigned char md[EVP_MAX_MD_SIZE];
	unsigned int md_len;
	
	
	mctx = EVP_MD_CTX_new();

	EVP_DigestInit_ex(mctx, digest, NULL);
	EVP_DigestUpdate(mctx, pP1, strlen(pP1));
	EVP_DigestUpdate(mctx, pP2, strlen(pP3));
	EVP_DigestUpdate(mctx, pP3, strlen(pP2));
	EVP_DigestFinal(mctx, md, &md_len);

	EVP_MD_CTX_free(mctx);

	string strName = "EVP ";
	strName += alg;
	PrintByteBuffer(strName.c_str(), md, md_len);
}


void TestEVP_Cypher()
{
	string				strInput = "Yoda said, Do or do not. There is no try.";
	string				strCyper, strOutput;
	SHA256_CTX			c;
	const char			*key = "This is my AES key by tebek... "\
						"One Little Two Little Three Little Endian? Indian!";
	unsigned char		keyCipher[32], iv[16];
	unsigned char		m[SHA256_DIGEST_LENGTH];
	EVP_CIPHER_CTX		*pCtxCipher;
	const EVP_CIPHER	*pCipher;
	CMyRand				rand(MT19937, UNIFORM_INT, 1, 0xFF);
	unsigned char		buf[128];
	unsigned char		bufEncrypted[128];
	unsigned char		bufOut[128];
	int					len_enc, len_dec, len_final;


	SHA256_Init(&c);
	SHA256_Update(&c, key, strlen(key));
	SHA256_Final(m, &c);
	OPENSSL_cleanse(&c, sizeof(c));

	memcpy(keyCipher, m, 32);
	memcpy(iv, m, 16);

	rand.Rand(buf, 78);

	pCtxCipher = EVP_CIPHER_CTX_new();
	pCipher = EVP_get_cipherbyname("aes-256-cfb");

	// Encrypt
	EVP_CipherInit(pCtxCipher, pCipher, keyCipher, iv, 1);
	
	EVP_CipherUpdate(pCtxCipher, bufEncrypted, &len_enc, buf, 78);
	len_final = len_enc;
	EVP_CipherFinal(pCtxCipher, bufEncrypted + len_enc, &len_enc);
	len_enc += len_final;

	// Decrypt
	EVP_CipherInit(pCtxCipher, pCipher, keyCipher, iv, 0);

	EVP_CipherUpdate(pCtxCipher, bufOut, &len_dec, bufEncrypted, len_enc);
	len_final = len_dec;
	EVP_CipherFinal(pCtxCipher, bufOut + len_dec, &len_dec);
	len_dec += len_final;

	EVP_CIPHER_CTX_free(pCtxCipher);

	PrintByteBuffer("aes-256-cfb input", buf, 78);
	PrintByteBuffer("aes-256-cfb encrypt", bufEncrypted, len_enc);
	PrintByteBuffer("aes-256-cfb decrypt", bufOut, len_dec);
}

 

 

첫번째 것은 Message Digest이고 두번째 것은 대칭키 암복호화이다.

 

두 사용법에 규칙이 있다.

 

// 구조를 깔끔하게 보고 확인하기 위해서 세세한 코드부분은 삭제하였다.
ctx = EVP_방식_CTX_new();

EVP_방식Init(ctx);
EVP_방식Update(ctx);
EVP_방식Update(ctx);
EVP_방식Update(ctx);
EVP_방식Final(ctx);

EVP_방식_CTX_free(ctx);

 

 

Context 생성 -> Init -> Update -> Final -> Context 해제

 

기본 흐름이 똑같은 것을 소스에서 확인하자.

 

 

여기서 Update는 한번만 쓸수도 있고 여러번 쓸수도 있는데 입력값을 나눠서 넣는 기능이다.

일반적으로 stream 처리시 대용량 데이터 처리시에 나눠서 넣는다.

 

 

이번에 작성된 코드중 AES 256 CFB가 실행된 화면은 다음과 같다.

 

 

AES256의 특성상 16바이트의 패딩이 적용된 암호화 데이터가 생성되는데 위 결과는 그렇지 않다.

CFB 모드는 패딩을 하지 않기 때문인 것을 참고하자

 

 

전체 소스를 볼수 있는 경로는 다음과 같다.

 

https://github.com/tebeknet/TestOpenSSL/blob/master/TestOpenSSL/TestOpenSSL.cpp

 

OpenSSL은 앞으로도 계속 저 GitHub 위치에 업데이트 된다.