사용자 도구

사이트 도구


kb:base64encoding

Base64 Encoding

MIME과 관련된 곳에서 자주 쓰이는 base64 인코딩/디코딩 모듈이다. 바이너리를 문자열로, 문자열을 바이너리로 변환시켜주는 알고리즘이다. MIME과 관련된 것은 아니지만, 암호화와 관련해서 필요할 때가 있는데, Win32에서는 XP에서만 문자열 인코딩 관련 API가 있기 때문에 이렇게 따로 만들게 되었다.

C++ Implementation

Base64.h
#ifndef __BASE64_H__
#define __BASE64_H__
 
////////////////////////////////////////////////////////////////////////////////
/// \class Base64
/// \brief 바이너리 값을 base64로 인코딩하거나, 또는 그 반대로 디코딩하는 일과 
/// 관련된 함수들을 모아놓은 클래스이다. 
///
/// 예를 들어 'ABCD'라는 스트림은 총 32비트다. 이는 다음과 같이 매핑된다.
/// 
/// <pre>
/// ABCD
/// 
///  A (65)     B (66)     C (67)     D (68)   (None) (None)
/// 01000001   01000010   01000011   01000100
/// 
/// 16 (Q)  20 (U)  9 (J)   3 (D)    17 (R) 0 (A)  NA (=) NA (=)
/// 010000  010100  001001  000011   010001 000000 000000 000000
/// 
/// QUJDRA==
/// </pre>
///
/// 유닛 테스트를 위한 케이스를 들어보자면 다음과 같다.
///  - 빈 문자열 :       ->          ->
///  - 1 글자    :  A    -> QQ==     -> A
///  - 2 글자    :  AB   -> QUJD     -> AB
///  - 3 글자    :  ABC  -> QUJD     -> ABC
///  - 4 글자    :  ABCD -> QUJDRA== -> ABCD
////////////////////////////////////////////////////////////////////////////////
 
class Base64
{
private:
    unsigned char* m_Text;       ///< NULL로 끝나는 문자열 버퍼
    unsigned char* m_Binary;     ///< 바이너리 버퍼
    size_t         m_binarySize; ///< 바이너리 버퍼의 길이
 
 
public:
    /// \brief 생성자
    Base64();
 
    /// \brief 소멸자
    ~Base64();
 
 
public:
    /// \brief 바이너리를 base64 문자열로 인코딩한다.
    const unsigned char* Encode(const unsigned char* binary, size_t binarySize);
 
    /// \brief base64 문자열을 바이너리로 디코딩한다.
    const unsigned char* Decode(const unsigned char* text, size_t& binarySize);
 
 
private:
    /// \brief 3개의 8비트 바이너리를 4개의 6비트 문자열로 인코딩한다.
    void EncodeBlock(unsigned char in[3], unsigned char out[4], int len);
 
    /// \brief 4개의 6비트 문자열을 3개의 8비트 바이너리로 디코딩한다.
    void DecodeBlock(unsigned char in[4], unsigned char out[3]);
 
    /// \brief 복사 생성자는 금지
    Base64(const Base64&) {}
 
    /// \brief 대입 연산자는 금지
    Base64& operator = (const Base64&) { return *this; }
};
 
#endif //__BASE64_H__
Base64.cpp
#include "Base64.h"
#include <stdio.h>
#include <stdlib.h>
#include <vector>
 
namespace
{
    // 인코딩을 위한 테이블 (RFC1113)
    const char EncodeTable[] = 
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
    // 디코딩을 위한 테이블
    const char DecodeTable[] = 
        "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
}
 
/// \brief 생성자
Base64::Base64()
: m_Binary(NULL), m_Text(NULL)
{
}
 
/// \brief 소멸자
Base64::~Base64()
{
    if (m_Binary != NULL) delete [] m_Binary;
    if (m_Text != NULL) delete [] m_Text;
}
 
/// \brief 바이너리를 base64 문자열로 인코딩한다.
const unsigned char* Base64::Encode(const unsigned char* binary, size_t binarySize)
{
    unsigned char in[3];
    unsigned char out[4];
    int len;
    int blocksout = 0;
    size_t current = 0;
    std::vector<unsigned char> intermediate;
 
    // 인코딩한다.
    while (current < binarySize)
    {
        len = 0;
 
        for (int i = 0; i < 3; i++) 
        {
            in[i] = binary[current];
            if (current < binarySize)
            {
                current++;
                len++;
            }
            else
            {
                in[i] = 0;
            }
        }
 
        if (len) 
        {
            EncodeBlock(in, out, len);
            for (int i = 0; i < 4; i++) 
                intermediate.push_back(out[i]);
        }
    }
 
    // 벡터에 있는 값을 텍스트 버퍼에 복사하기 이전에,
    // 기존의 텍스트 버퍼를 삭제하고, 벡터의 크기만큼 메모리를 재할당
    if (m_Text != NULL) delete [] m_Text;
    m_Text = new unsigned char[intermediate.size()+1];
    memset(m_Text, 0, sizeof(unsigned char) * (intermediate.size()+1));
 
    // 벡터에 있는 내용을 텍스트 버퍼에 복사
    for (size_t r=0; r<intermediate.size(); r++)
        m_Text[r] = intermediate[r];
 
    return m_Text;
}
 
/// \brief base64 문자열을 바이너리로 디코딩한다.
const unsigned char* Base64::Decode(const unsigned char* text, size_t& binarySize)
{
    unsigned char in[4];
    unsigned char out[3];
    unsigned char v;
    int i;
    int len;
    size_t current = 0;
    std::vector<unsigned char> intermediate;
    size_t TextSize = strlen((const char*)text);
 
    while (current < TextSize) 
    {
        for (i = 0, len = 0; i < 4 && current < TextSize; i++) 
        {
            v = 0;
 
            while (current < TextSize && v == 0) 
            {
                v = text[current];
                if (current < TextSize)
                    current++;
 
                v = (unsigned char) ((v < 43 || v > 122) ? 0 : DecodeTable[v - 43]);
                if (v) { v = (unsigned char) ((v == '$') ? 0 : v - 61); }
            }
 
            if (current < TextSize) 
            {
                len++;
                if (v) { in[i] = (unsigned char) (v - 1); }
            }
            else 
            {
                in[i] = 0;
            }
        }
 
        if (len) 
        {
            DecodeBlock(in, out);
            for (i = 0; i < len - 1; i++) 
                intermediate.push_back(out[i]);
        }
    }
 
    // 중간 버퍼에 있는 값을 바이너리 버퍼로 옮겨야한다.
    // 기존의 바이너리 버퍼를 삭제하고, 벡터의 크기만큼 메모리를 재할당
    // 서비스로 +1 크기만큼의 버퍼를 만들어 끝에다 0을 집어넣어준다.
    if (m_Binary != NULL) delete [] m_Binary;
    m_Binary = new unsigned char[intermediate.size()+1];
    memset(m_Binary, 0, sizeof(unsigned char) * (intermediate.size()+1));
 
    // 벡터에 있는 내용을 바이너리 버퍼에 복사
    for (size_t r=0; r<intermediate.size(); r++)
        m_Binary[r] = intermediate[r];
 
    // 사이즈 변수를 세팅
    binarySize = intermediate.size();
 
    return m_Binary;
}
 
/// \brief 3개의 8비트 바이너리를 4개의 6비트 문자열로 인코딩한다.
void Base64::EncodeBlock(unsigned char in[3], unsigned char out[4], int len)
{
    out[0] = EncodeTable[in[0] >> 2];
    out[1] = EncodeTable[((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)];
    out[2] = (unsigned char) (len > 1 ? EncodeTable[((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6)] : '=');
    out[3] = (unsigned char) (len > 2 ? EncodeTable[in[2] & 0x3f] : '=');
}
 
/// \brief 4개의 6비트 문자열을 3개의 8비트 바이너리로 디코딩한다.
void Base64::DecodeBlock(unsigned char in[4], unsigned char out[3])
{
    out[0] = (unsigned char) (in[0] << 2 | in[1] >> 4);
    out[1] = (unsigned char) (in[1] << 4 | in[2] >> 2);
    out[2] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
}

링크

kb/base64encoding.txt · 마지막으로 수정됨: 2014/11/10 19:50 (바깥 편집)