概要

Base64はデータを64種類の大文字及び小文字の英字と数字及び+と/に割り当てるエンコード方法でありMIMEに規定されている。
電子メールの認証や電子メールでバイナリー形式を扱う場合やhttpのBasic認証に用いられる。
本プログラムはBase64 testという文字列をBase64でエンコードしその結果をデコードするのものである。
コマンドプロンプトで動作し、エンコード結果とデコード結果を表示する。

テスト環境

コンパイラ

Visual C++ 2013 Express 32bit/64bit

実行環境

Windows 7 Enterprise Service Pack 1 64bit(Sandy Bridge-E)

Base64でエンコードする方法

概要

Base64は入力データを6bitずつに分解し、6bitの0~63の値に以下の文字を当てはめたものである。
変換前の10の桁             1         2         3         4         5         6
変換前の 1の桁   0123456789012345678901234567890123456789012345678901234567890123
変換後の文字     ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
ANSI文字は8bitなので8と6の最小公倍数である24bit(3byte)単位で処理すると都合がよい。

変換例

入力文字列 ABCDEFG
上記を16進数及び2進数で表現すると以下のとおりである。
16進数 0x41 0x42 0x43 0x44 0x45 0x46 0x47
2進数  01000001-01000010-01000011-01000100-01000101-01000110-01000111-0000
6bit単位で分割した場合の最後6bitに満たない場合は下位に0を追加して6bitにする。
6bit単位を10進数で表すと
10進数 16 20 9 3 17 4 21 6 17 48
0~63の値に対して文字を割り当てる。
QUJDREVGRw
文字列の長さが4の倍数でない場合は、末尾に=を追加して4の倍数に合わせる。
QUJDREVGRw==

プログラムソースの概要

main

変数srcにbase64へエンコードしたい文字列を指定します。
base64Encode関数によりbase64へエンコードして変数b64に格納します。
base64Decode関数により変数b64の文字列をデコードします。結果は変数srcと同じとなります。

base64Encode

文字数に一番近い3の倍数の文字数(文字数より小さい)に対して以下の処理をします。

3文字分(3byte)を抽出して24bitの整数値にします。
24bitの上位から6bitずつ切り出し、o0~o3に代入します。
テーブルによりo0~o3を文字に変換し先頭から格納します。 次の3文字に対して上記の処理を繰り返します。

文字数を3で割ったときの余りの文字が無い場合は、変換後の文字の末尾に0を付加して終了します。

文字数を3で割ったときの余りの文字が1文字の時

1byteの上位6bitをo0に代入,1byteの下位2bitをo1の上位2bit残り4bitを0とし、テーブルによりo0~o1を文字に変換します。
変換後の文字の末尾に=を2文字と0を付加して終了します。

文字数を3で割ったときの余りの文字が2文字の時

文字2byteと0を合成して24bit値を作成し、上位から6bitずつ分割しo0~o2に代入します。
テーブルによりo0~o1を文字に変換します。
変換後の文字の末尾に=を1文字と0を付加して終了します。

base64Decode

エンコード時に文字数がちょうど4の倍数になるように調整されているので、4文字ずつ切り出して処理ができます。
各文字をbase64_to_6bit関数により文字に対する6bitの値を算出します。
4個の6bit値を24bit(3byte)の整数に変換します。

base64_to_6bit

Base64文字から6bitの整数に変換します。引数が文字=の場合は0を返します。C言語の文字列は、終了文字が0なので害はありません。

ソースコード

#include <stdio.h>
#include <string.h>


//	base64の1文字を6bitの値に変換する

inline int base64_to_6bit(int c){
	if (c == '=')
		return 0;
	if (c == '/')
		return 63;
	if (c == '+')
		return 62;
	if (c <= '9')
		return (c - '0') + 52;
	if ('a'<=c)
		return (c - 'a') + 26;
	return (c - 'A');
}

//	base64の文字列srcをデコードしてバイナリ値に変換しdtcに格納
//	lenはbase64の文字数
//	変換後のバイト数を返す

int base64Decode(char* src, char* dtc){
	unsigned o0, o1, o2, o3;
	char* p = dtc;
	for (int n = 0; src[n];){
		o0 = base64_to_6bit(src[n]);
		o1 = base64_to_6bit(src[n + 1]);
		o2 = base64_to_6bit(src[n + 2]);
		o3 = base64_to_6bit(src[n + 3]);

		*p++ = (o0 << 2) | ((o1 & 0x30) >> 4);
		*p++ = ((o1 & 0xf) << 4) | ((o2 & 0x3c) >> 2);
		*p++ = ((o2 & 0x3) << 6) | o3 & 0x3f;
		n += 4;
	}
	*p = 0;
	return int(p - dtc);
}

//	バイナリの配列srcをbase64でエンコードする
//	src:バイナリ配列 len:バイナリ配列長さ
//	dtc:変換後の文字列を保存するアドレス dtc_len:変換後のバイト数を保存するアドレス

void base64Encode(char* src, char* dtc, int len, int* dtc_len){
	//	6bitからbase64の文字へ変換するテーブル
	//                              1         2         3         4         5         6
	//                    0123456789012345678901234567890123456789012345678901234567890123456789
	//                                    1               2               3
	//                    0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
	const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

	int n;
	int mod = len % 3;

	int adj_len = len - mod;	//	3の倍数に修正
	char* p = dtc;
	int o0, o1, o2, o3;
	for (n = 0; n < adj_len;){
		unsigned x = ((unsigned)src[n] << 16) + ((unsigned)src[n + 1] << 8) + unsigned(src[n + 2]);
		o0 = (x >> 18) & 0x3f;
		o1 = (x >> 12) & 0x3f;
		o2 = (x >> 6) & 0x3f;
		o3 = x & 0x3f;
		*p++ = table[o0];
		*p++ = table[o1];
		*p++ = table[o2];
		*p++ = table[o3];
		n += 3;
	}
	if (mod){
		if (mod==1){
			unsigned x = (unsigned)src[n] << 16;
			o0 = (x >> 18) & 0x3f;
			o1 = (x >> 12) & 0x3f;
			*p++ = table[o0];
			*p++ = table[o1];
			*p++ = '=';
			*p++ = '=';
		}
		else if (mod==2){
			unsigned x = ((unsigned)src[n] << 16) + ((unsigned)src[n + 1] << 8) ;
			o0 = (x >> 18) & 0x3f;
			o1 = (x >> 12) & 0x3f;
			o2 = (x >> 6) & 0x3f;
			*p++ = table[o0];
			*p++ = table[o1];
			*p++ = table[o2];
			*p++ = '=';
		}
	}
	*p = 0;
	*dtc_len = int(p - dtc);
}

void main(void){
	char* src = "Base64 test";
	char b64[32];
	char dtc[32];
	int len;
	base64Encode(src, b64, (int)strlen(src), &len);
	base64Decode(b64, dtc);
	printf("base64Encode:%s\n", b64);
	printf("base64Decode:%s\n", dtc);
}

実行ファイルとソースファイルのダウンロード(Visual C++ 2013 マルチバイトでコンパイル)