SHA1 해쉬 함수의 경우 crypto++ 라이브러리로 사용 가능 하지만, 간단히(?) boost 라이브러리도 가능.

http://www.cryptopp.com/



#include <iostream>
#include <string>
using namespace std;

#include <boost/uuid/sha1.hpp>
#include <boost/format.hpp>
 
 
string ToSHA1(const string s)
{
    boost::uuids::detail::sha1 sh;
    sh.process_bytes(s.c_str(), s.length());

    unsigned int digest[5];
    sh.get_digest(digest);

    string r;
    for(int i = 0; i < 5; ++i) {
        r += str(boost::format("%08x") % digest[i]);
    }

    return r;
}

int main()
{
    
    try {
        cout << ToSHA1("abcedfg") << endl;

    }
    catch (exception &e) {
        cout << e.what() << endl;
    }
    catch (...) {
        cout << "unknown error" << endl;
    }

    return 0;
}



# 큰수를 나타내기 위해 boost::multiprecision 의 cpp_int 클래스 이용
http://www.boost.org/doc/libs/1_56_0/libs/multiprecision/doc/html/boost_multiprecision/tut/ints/cpp_int.html


# 큰수를 나타내는 boost 지원 3개의 클래스 비교
http://www.boost.org/doc/libs/1_56_0/libs/multiprecision/doc/html/boost_multiprecision/tut/ints.html


# 소수 판별을 위한 boost::multiprecision 의 miller_rabin_test 함수 사용 


# 밀러 라빈 소수 판별법 
http://ko.wikipedia.org/wiki/%EB%B0%80%EB%9F%AC-%EB%9D%BC%EB%B9%88_%EC%86%8C%EC%88%98%ED%8C%90%EB%B3%84%EB%B2%95


# Random 클래스는 C++11의 표준을 사용함 



#include <iostream>
#include <algorithm>
#include <limits>
#include <random>
#include <sstream>
using namespace std;

#include <boost/format.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/miller_rabin.hpp>
namespace bmp = boost::multiprecision;
typedef bmp::cpp_int INT;


INT RND_INT(int bytes)
{
    random_device rd;   
    std::mt19937 rnd(rd());
    INT r;

    if (bytes < 8) {
        std::uniform_int_distribution<uint32_t> dist(1, numeric_limits<uint32_t>().max());
        r = dist(rnd);
    }
    else {
        std::uniform_int_distribution<uint64_t> dist(1, numeric_limits<uint64_t>().max());
        int loop = bytes / 8;

        for (int i = 0; i < loop; ++i) {
            r |= (INT(dist(rnd)) << (64 * i));
        }
    }

    return r;
}

INT RND_Prime(int bytes)
{
    INT r(0);

    for (int i = 0; i < 1024; ++i) {
        INT n = RND_INT(bytes);

        bool b = bmp::miller_rabin_test(n, 25);
        if (b) {
            r = n;
            cout << "find : " << i << ", prime nmber is " <<  endl;
            cout << str(boost::format("%X") % n) << endl << endl;
            break;
        }   
    }

    return r;
}



void DHC_TEST()
{
    INT P = RND_Prime(16);
    if (P == 0) {
        throw logic_error("DHC_TEST : prime number failed ");
    }
    const INT G = RND_INT(8);

    const INT privateA = RND_INT(8);
    const INT privateB = RND_INT(8);

    cout << "ali private : " << endl <<  str(boost::format("%X") % privateA) << endl;
    cout << "bob private : " << endl <<  str(boost::format("%X") % privateB) << endl;
    cout << endl;

    auto publicA = bmp::powm(G, privateA, P);       
    auto publicB = bmp::powm(G, privateB, P);

    cout << "ali public : " << endl <<  str(boost::format("%X") % publicA) << endl;
    cout << "bob public : " << endl <<  str(boost::format("%X") % publicB) << endl;
    cout << endl;

    cout << "ali block key : " << endl <<  str(boost::format("%032X") % bmp::powm(publicB, privateA, P)) << endl;
    cout << "bob block key : " << endl <<  str(boost::format("%032X") % bmp::powm(publicA, privateB, P)) << endl;

}


int main()
{
    
    try {
        DHC_TEST(); 
    }
    catch (exception &e) {
        cout << e.what() << endl;
    }
    catch (...) {
        cout << "unknown error" << endl;
    }

    return 0;
}


출처 : 위키백과 http://ko.wikipedia.org/wiki/%EB%94%94%ED%94%BC-%ED%97%AC%EB%A7%8C_%ED%82%A4_%EA%B5%90%ED%99%98


디피-헬만 키 교환

위키백과, 우리 모두의 백과사전.

디피-헬만 키 교환(Diffie–Hellman key exchange)은 암호 키를 교환하는 하나의 방법으로, 두 사람이 암호화되지 않은 통신망을 통해 공통의 비밀 키를 공유할 수 있도록 한다. 휫필드 디피와 마틴 헬만이 1976년에 발표하였다.

디피-헬만 키 교환은 기초적인 암호학적 통신 방법을 수립하였으며, 이후 1977년 공개 키 암호 방식인 RSA 암호가 제안되었다.

방식[편집]

앨리스와 밥이 공개된 통신망에서 디피-헬만 키 교환을 하기 위해서는 다음과 같은 절차를 거친다.

  1. 앨리스가 소수 p, 그리고 1부터 p-1까지의 정수 g를 선택하여 사전에 밥과 공유한다.
  2. 앨리스가 정수 a를 선택한다. 이 정수는 외부에 공개되지 않으며, 밥 또한 알 수 없다.
  3. 앨리스가 A = g^a \text{ mod }p, 즉 g^a를 p로 나눈 나머지를 계산한다.
  4. 밥이 마찬가지로 정수 b를 선택하여 B = g^b \text{ mod }p를 계산한다.
  5. 앨리스와 밥이 서로에게 A와 B를 전송한다.
  6. 앨리스가 B^a \text{ mod }p를, 밥이 A^b \text{ mod }p를 계산한다.

마지막 단계에서 B^a = (g^b)^a = g^{ab}A^b = (g^a)^b = g^{ab}이며 따라서 앨리스와 밥은 g^{ab} \text{ mod }p라는 공통의 비밀 키를 공유하게 된다.

앨리스와 밥 이외의 인물은 a와 b를 알 수 없으며, g, p, g^a \text{ mod }p, g^b \text{ mod }p를 알 수 있다.

예제[편집]

이 과정을 실제 숫자를 통해 예를 들면 다음과 같다. 여기서는 설명을 위해 작은 크기의 소수를 사용하지만, 실제 응용에서는 안전을 위해 10진수 수백~수천자리 크기의 큰 소수를 사용한다. 공개된 정보는 파란색으로, 비밀 정보는 붉은색 굵은 글씨로 표시하였다.

  1. 앨리스와 밥은 p=23g=5를 사용하기로 합의한다.
  2. 앨리스가 비밀 정보를 전송하기 위해 임의의 정수 a=6을 고른 후, 밥에게 A = ga mod p 을 전송한다.
    • A = 56 mod 23
    • A = 15,625 mod 23
    • A = 8
  3. 밥은 임의의 정수 b=15 를 고르고, 앨리스에게 B = gb mod p 를 전송한다.
    • B = 515 mod 23
    • B = 30,517,578,125 mod 23
    • B = 19
  4. 앨리스는 밥에게서 받은 B 를 바탕으로 s = B a mod p 를 계산한다.
    • s = 196 mod 23
    • s = 47,045,881 mod 23
    • s = 2
  5. 밥은 앨리스에게서 받은 A 를 바탕으로 s = A b mod p 를 계산한다.
    • s = 815 mod 23
    • s = 35,184,372,088,832 mod 23
    • s = 2
  6. 앨리스와 밥은 이제 비밀 키 s = 2 를 공유하게 되었다.

여기서 p가 충분히 클 경우, 외부에서 비밀 키를 알아내기 위해 도청을 하는 도청자 이브는 g^a나 g^b를 통해 s를 알아낼 수 없는 것으로 알려져 있다. 앨리스와 밥은 두 사람 만이 아는 비밀 키 s를 갖게 되었으므로, 대칭 키 암호를 이용해 이후의 통신을 암호화할 수 있다.

그러나 p나 ab가 너무 작을 경우, 도청자는 가능한 모든 조합을 다 계산해보는 방식으로 s를 계산해낼 수 있다. 따라서 실제 비밀 통신에는 충분히 큰 소수를 사용해야 한다. 만약 p가 최소 300자리의 소수이고, a와 b가 각각 100자리 이상의 정수일 경우, 현재 인류가 보유한 모든 컴퓨터를 동원해도 공개된 정보로부터 비밀 키를 알아낼 수 없는 것으로 알려져 있다.

안전성[편집]

디피-헬만 키 교환은 소수 p와 g를 적절하게 고르면 도청에 대해 안전한 것으로 알려져 있다. 도청자 이브가 비밀키를 얻어내기 위해서는 앨리스와  사이의 통신에서 도청할 수 있는 정보인 g^a와 g^b로부터 g^{ab}를 구해야 한다. 이 문제를 디피-헬만 문제(Diffie-Hellman problem)로 부르며, 이 문제를 푸는 효율적인 알고리즘은 2013년 현재 알려지지 않았다. 이산 로그 문제를 효율적으로 풀 수 있을 경우 디피-헬만 문제 또한 효율적으로 풀 수 있지만, 그 역이 참인지는 알려지지 않았다.

안전한 키 교환을 위해서는 p와 g를 신중하게 선택해야 한다. g는 순환군 G의 차수가 소수이거나, 인수분해하기 어려운 큰 소수를 약수로 갖도록 해야 한다. 이 때문에 p와 \frac{p-1}{2}이 모두 소수인 안전 소수를 고르기도 한다. p가 안전 소수일 경우 G의 차수는 2 또는 \frac{p-1}{2}만을 약수로 갖게 된다.

또한 앨리스와 밥이 충분히 안전하지 못한 난수 생성 알고리즘을 사용할 경우, 공격자는 이를 이용해 다음 a와 b의 특성을 어느정도 예측할 수 있다.

디피-헬만 키 교환은 통신을 하는 대상과 비밀 정보를 공유할 수 있지만, 상대방에 대한 인증은 보장되지 않으며 중간자 공격이 가능하다. 앨리스와 밥이 상대방에 대한 인증을 하지 못할 경우, 공격자는 중간에서 통신을 가로채 앨리스와 공격자, 그리고 공격자와 밥 사이에 각각 두 개의 디피 헬만 키 교환을 생성하고, 앨리스와 밥이 각각 서로와 통신을 하는 것처럼 위장할 수 있다. 이와 같은 종류의 중간자 공격을 막기 위한 여러가지 다른 알고리즘이 개발되어 있다.



출처 : http://eastdg.wordpress.com/2014/04/09/ssltls-%EA%B8%B0%EB%B3%B8/


Key Exchange (키 교환)

SSL/TLS이 사용할 수 있는 몇몇 키 알고리즘들이 있는데, 대부분의 키 알고리즘들은 서버의 공개키를 이용하여 동작한다. 다음은 많이 쓰이는 키 알고리즘 들이다.

  • RSA: 서버의 키…….유형이 RSA 형식인 경우 사용가능하다. 클라이언트는 46바이트의 랜덤 값과 2바이트 버전을 포함한 총 48바이트의 “pre-master secret” 값을 만들어 서버의 공개키로 암호화 하여 전송한다. 이러한 경우 ServerKeyExchange 과정은 없다.
  • DHE_RSA: 서버의 키 유형이 RSA 형식인 경우지만, 해당 키는 서명을 하는 경우에만 사용된다. 실제 키 교환은 Diffie-Hellman 알고리즘을 이용하여 교환하는데, 이 경우 서버는 DH 인자값들(modulus, generator)과 DH용 공개키를 포함한 ServerKeyExchange 메시지를 보낸다. (아직까진 이걸로 추천)
  • DHE_DSS: DHE_RSA 키 알고리즘과 유사하게 동작하지만 서버가 DSS 키를 가지고 있는 경우 사용된다. DSS는 DSA로도 알려져 있으며 서명에만 쓰인다.

다음은 보안상이나 기타 다른 이유로 잘 쓰이지 않는 키 알고리즘들이다.

  • DH: 서버의 키 유형이 Diffie-Hellman 유형인 경우 사용된다.
  • DH_anon: DHE와 비슷하지만 서버의 서명이 없다. 서버의 인증서 없이 동작되기 때문에 MITM 공격에 취약하다.
  • PSK: 키를 이미 기타 다른 방법으로 공유한 경우 사용되는 키 알고리즘이다.
  • SRP: application of the SRP protocol which is a Password Authenticated Key Exchange protocol. Client and certificate authenticate each other with regards to a shared secret, which can be a low-entropy password (whereas PSK requires a high-entropy shared secret). Very nifty. Not widely supported yet.
  • An ephermeral RSA key: DHE와 비슷하지만 RSA 키쌍을 생성한다. 그렇기 때문에 성능 부분에 대해 꽤 비싼 비용을 지불하며, 또 그렇게 매력적인 옵션은 아니다.
  • ECDH_ECDSA, ECDH_RSA, ECDHE_ECDSA, ECDHE_RSA, ECDH_anon: elliptic curves를 이용한 다양한 DH* 알고리즘들로서 가장 강력하며 추후 표준이 될 수도 있다. (ECDH_anon은 제외)

현 시점에서는 elliptic curves를 이용한 DH* 알고리즘을 지원하는 클라이언트가 그리 많지 않다. 그리고 대부분의 클라이언트가 RSA 또는 DHE_RSA를 지원하기 때문에 이 2개를 먼저 고려하게 될 것이다. 이 2개중 어느것이 더 괜찮냐고 물어본다면 DHE_RSA가 더 괜찮다. 이유인즉 RSA경우 만약에 공격자가 서버의 비밀키를 획득하게 된다면 이전의 모든 SSL/TLS 전문들을 다시 복호화 할 수 있기 때문이다. DHE_RSA인 경우에는 별도로 교환된 키로 암복호화 하기 때문에 이전의 SSL/TLS 전문들을 다시 복호화 하기는 힘들다.



SHA1CryptoServiceProvider는 윈도우의 CryptoAPI(CAPI)를 래핑한 unmanaged 구현체 이고,
SHA1Managed는 .NET으로 구현한 Managed 코드


SHA1Managed가 상대적으로 느리다고 하는데, 그 차이는 ms 단위의 속도를 중요시 하지 않는 업무라면 문제 없을 듯 하고,
자체 구현이라 이식성도 좋아 보임


물론 사용 가능 하다면 SHA1CryptoServiceProvider 쓰는것이 최선..



http://codeissue.com/issues/i34dda6deaad90a/difference-between-sha1-sha1cryptoserviceprovider-sha1managed-and-sha1cng


SHA1CryptoServiceProvider: this is wrapper for unmanaged CryptoAPI(CAPI). This is Federal Information Processing Standard (FIPS) certified.

SHA1Managed: this is complete implementation of SHA1 using managed code. This is fully managed but not FIPS certified and may be slower.




2021년 신규 작성 

CryotoPP Example - cdeclog

 

CryotoPP Example

CryptoPP https://www.cryptopp.com/ Crypto++ 사용하기, 예제 암호화 지식 (개발자)

cdecl.github.io

 

--

C++ 용 암호화 라이브러리
 
Crypto++ 컴파일 
 
우선 해당 사이트에서 라이브러리를 다운 받은 후 원하는 컴파일러로 컴파일을 하여 dll 이나 lib 파일을 생성한다.
VC++ 2008 버전의 경우 프로젝트 파일이 따로 있지는 않고 VC++2005 버전을 업그레이드 후 컴파일 하면 됨
 
컴파일을 하면 cryptopp560\Win32 디렉토리에 각각 파일이 생성되고, dll 의 경우는 cryptopp560\Win32\DLL_Output 폴더에, static library 의 경우는 cryptopp560\Win32\Output 폴더에 각각의 모듈이 생성 된다. 
 
내가 테스트 할때는 dll 의 버전의 경우 이상하게 링크에러가 발생을 하여,
static library 를 사용하여 테스트를 하고 어플리케이션의 경우도 static library 로 컴파일을 해야 문제가 없었다.
 
블록 알고리즘 고려사항 
알고리즘 종류, 블록 알고리즘 운영 모드, 패딩종류, 인코딩 (텍스트 암호화의 경우)
 
알고리즘 SEED, AES, DES, 3-DES 설명 
 
 
DES
가장 오래되고, 세계적으로 가장 널리 사용되는 고전적 암호화 알고리즘이다. 파일이나 패킷을 암호화할 때 많이 사용된다. 하지만, 64비트 입력 블록과 56비트 짧은 비밀키를 사용하기 때문에, 더 이상 안전하지 않다고 간주하고 있다. 그러나, 국가 기밀을 다룰 정도로 극히 중요한 보안이 아니라면, 여전히 가장 널리 사용되는 알고리즘이다.
 
3-DES
DES를 3번 반복해서 암호화한다. 보안성이 향상되고, 그 만큼 성능은 떨어진다.
 
AES
미국 NIST에서 공모해서 표준화한 새로운 알고리즘이다. 128비트 입력 블록을 도입함으로써, 보안성을 향상했으며, 최근에 세계적으로 널리 사용되는 알고리즘이다.
 
SEED
KISA 주관으로 ETRI와 함께 국내에서 만들어진 알고리즘이다. 역시, 128비트 입력 블록을 사용하고 있고, 국제 표준에 부합하는 알고리즘이다.
 
 
 
 
블록 알고리즘 운영 모드 
 
ECB(Electronic codebook) : 평문을 일정 크기의 블록으로 나누어서 처리, 각 블록은 동일한 키로 암호
CBC(Cipher-block chaining) : 평문 블록과 바로 직전의 암호블록을 XOR한 것. 첫번째 암호 블록을 위해 초기 벡터 IV 값 사용
 
기타..
PCBC(Propagating cipher-block chaining)
CFB(Cipher feedback)
OFB(Output feedback)
CTR(Counter)
 
 
패딩(Padding) 종류
 
enum BlockPaddingScheme 
{NO_PADDING, ZEROS_PADDING, PKCS_PADDING, ONE_AND_ZEROS_PADDING, DEFAULT_PADDING};
 
NO_PADDING : 패딩 없음
ZEROS_PADDING : NULL(0) 으로 패딩 
PKCS_PADDING : 패딩되는 바이트의 수의 같은 값으로 모두 패딩
ONE_AND_ZEROS_PADDING :  ONE_AND_ZEROS_PADDING to use 0x80 instead 0x01 as padding
DEFAULT_PADDING : DEFAULT_PADDING means PKCS_PADDING 
  if c.MandatoryBlockSize() > 1 && c.MinLastBlockSize() == 0 (e.g. ECB or CBC mode), otherwise NO_PADDING (OFB, CFB, CTR, CBC-CTS modes)
 
 
예제, 필요한 헤더파일 (SEED, AES, DES 사용시..)
#include <iostream>
using namespace std;

//#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1   

// Crypto++ Includes
#include "cryptlib.h"
#include "Base64.h"
#include "aes.h"        
#include "seed.h"
#include "des.h"
#include "modes.h"      
#include "filters.h"    

#pragma comment(lib, "cryptlib")  

template <class TyMode>
std::string Encrypt(TyMode &Encryptor, const std::string &PlainText)
{
	std::string EncodedText;

	try {
		CryptoPP::StringSource(PlainText, true,
			new CryptoPP::StreamTransformationFilter(Encryptor,
				new CryptoPP::Base64Encoder(
					new CryptoPP::StringSink(EncodedText), false
					), CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING
				)
			);
	}
	catch (...) {}

	return EncodedText;
}

template <class TyMode>
std::string Decrypt(TyMode &Decryptor, const std::string &EncodedText)
{
	std::string RecoveredText;

	try {
		CryptoPP::StringSource(EncodedText, true,
			new CryptoPP::Base64Decoder(
				new CryptoPP::StreamTransformationFilter(Decryptor,
					new CryptoPP::StringSink(RecoveredText),
					CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING
					)
				)
			);
	}
	catch (...) {}

	return RecoveredText;
}

template <class Ty>
std::string CBC_Encrypt(byte *KEY, byte *IV, const std::string &PlainText)
{
	typename CryptoPP::CBC_Mode<Ty>::Encryption Encryptor(KEY, Ty::DEFAULT_KEYLENGTH, IV);
	return Encrypt(Encryptor, PlainText);
}


template <class Ty>
std::string CBC_Decrypt(byte *KEY, byte *IV, const std::string &PlainText)
{
	typename CryptoPP::CBC_Mode<Ty>::Decryption Decryptor(KEY, Ty::DEFAULT_KEYLENGTH, IV);
	return Decrypt(Decryptor, PlainText);
}

template <class Ty>
std::string ECB_Encrypt(byte *KEY, const std::string &PlainText)
{
	typename CryptoPP::ECB_Mode<Ty>::Encryption Encryptor(KEY, Ty::DEFAULT_KEYLENGTH);
	return Encrypt(Encryptor, PlainText);
}


template <class Ty>
std::string ECB_Decrypt(byte *KEY, const std::string &PlainText)
{
	typename CryptoPP::ECB_Mode<Ty>::Decryption Decryptor(KEY, Ty::DEFAULT_KEYLENGTH);
	return Decrypt(Decryptor, PlainText);
}


template <class CryptoType>
void Test()
{
	using namespace std;

	const std::string sText = "Plain Text";
	std::string sEnc, sDec;

	byte KEY[CryptoType::DEFAULT_KEYLENGTH] = { 0, };
	byte IV[CryptoType::BLOCKSIZE] = { 0x01, };

	// CBC 모드
	sEnc = CBC_Encrypt<CryptoType>(KEY, IV, sText);
	sDec = CBC_Decrypt<CryptoType>(KEY, IV, sEnc);

	cout << CryptoType::StaticAlgorithmName() << " : " << "CBC_MODE" << endl;
	cout << sText << "\n -> " << sEnc << "\n -> " << sDec << endl;


	// ECB 모드
	sEnc = ECB_Encrypt<CryptoType>(KEY, sText);
	sDec = ECB_Decrypt<CryptoType>(KEY, sEnc);

	cout << CryptoType::StaticAlgorithmName() << " : " << "ECB_MODE" << endl;
	cout << sText << "\n -> " << sEnc << "\n -> " << sDec << endl;
	cout << endl;
}


int main()
{
	using namespace std;

	// SEED 
	Test<CryptoPP::SEED>();

	// AES 
	Test<CryptoPP::AES>();

	// DES 
	Test<CryptoPP::DES>();

	return 0;
}
  
 

 

  1. lmg2738 2009.11.09 16:08

    ^^ 안녕하세요~ 이민규입니다.

    하도 오래 전에 떠나 어떻게 직책을 불러들여야 할지 모르겠네요. 팀장님 맞죠 ?ㅋ

    구글에서 Seed C# 이라고 검색했더니 팀장님 트위터 나오더라구요.. ㅋ

    링크타고 티스토리도 다녀갑니다. 좋은 하루 되세요~~

  2. imagej 2016.10.18 14:33

    안녕하세요 AES 다기종 암호화 서칭 중 정보를 얻어 갑니다.

    좋은 정보 감사드리구요.

    그런데 Base64Decoder 라는 함수가 존재를 않하는데 원인을 모르겠군요.

+ Recent posts