[ MINI SERIES ]
RFC 2045 ?
Base64 の仕組み
2002/07/02 Tomohiro Kumagai
□ Base64
Base64 は、メールなどの MIME ドキュメントで、バイナリデータなど、8 ビットデータを含む文書を、ASCII 文字列 (7 ビット) に変換するための仕組みのひとつです。
今回は、データを Base64 にエンコードする方法を調べてみました。
なるべく正しいことを書くつもりですが、この道に詳しいわけではないので間違っている可能性もあります。ので、他で解説されている内容の補足程度にでもご利用ください。
□ エンコード
エンコードの仕組み
エンコード方法は、一定のルールに従って、3 バイト ( 8bit ) データ を、6 ビットずつに分解し、それらを 4 バイトに変換します。変換後に 1 バイト大きくなるのは、バイナリデータを ASCII コードで表現できるようにするためです。
この変換の際に、64 進数を利用します。この base64 で使用する 64 進数は、算用数字では表現できないので、次のように対応付けられたアルファベットを利用します。
| |
0x |
1x |
2x |
3x |
| x0 |
A |
Q |
g |
w |
| x1 |
B |
R |
h |
x |
| x2 |
C |
S |
i |
y |
| x3 |
D |
T |
j |
z |
| x4 |
E |
U |
k |
0 |
| x5 |
F |
V |
l |
1 |
| x6 |
G |
W |
m |
2 |
| x7 |
H |
X |
n |
3 |
| x8 |
I |
Y |
o |
4 |
| x9 |
J |
Z |
p |
5 |
| xA |
K |
a |
q |
6 |
| xB |
L |
b |
r |
7 |
| xC |
M |
c |
s |
8 |
| xD |
N |
d |
t |
9 |
| xE |
O |
e |
u |
+ |
| xF |
P |
f |
v |
/ |
このほか、特別な意味として、"=" も利用します。
これらの記号を利用して、符号化を行います。なお、符号化後の文字数は 76 文字以内でなくてはいけないようです。
エンコードは 6 ビット単位で行いますが、最後のデータで 6 ビットに満たないような部分が出てきた場合は、右側を 0 で埋めます。
また、出力は 4 バイト単位ですが、最後のデータでそれに満たない場合には、4 バイト単位になるように "=" を付加します。
エンコードの例
"EZ-NET" のエンコード
文字列 "EZ-NET" を、2進数に直してみます。そしてそれらを、6 ビットごとのグループに分けます。24 ビットずつなので、"EZ-" と "NET" の2回に分けての変換となります。
まずは "EZ-" から。
| 文字
| E
| Z
| -
|
| ASCII 値
| 0x45
| 0x5A
| 0x2D
|
| 8 bit
| 01
| 00
| 01
| 01
| 01
| 01
| 10
| 10
| 00
| 10
| 11
| 01
|
| 6 bit
| 01
| 00
| 01
| 01
| 01
| 01
| 10
| 10
| 00
| 10
| 11
| 01
|
| 値
| 0x11
| 0x15
| 0x28
| 0x2D
|
| 64進数
| R
| V
| o
| t
|
'E' と 'Z' と '-' をそれぞれ ASCII コードに直します。それらを 6 ビットごとのくくりに分けて、その値に対応づけられたアルファベットに置き換えます。
そして "NET" です。これも先ほどの場合と変わりありません。
| 文字
| N
| E
| T
|
| ASCII 値
| 0x4E
| 0x45
| 0x54
|
| 8 bit
| 01
| 00
| 11
| 10
| 01
| 00
| 01
| 01
| 01
| 01
| 01
| 00
|
| 6 bit
| 01
| 00
| 11
| 10
| 01
| 00
| 01
| 01
| 01
| 01
| 01
| 00
|
| 値
| 0x13
| 0x24
| 0x15
| 0x14
|
| 64進数
| T
| k
| V
| U
|
これでエンコード完了です。"EZ-NET" は、Base64 エンコードすることで "RVotTkVU" となりました。
"Program" のエンコード
続いて "Program" という文字列をエンコードする場合です。
これを 24 ビットごとに分けると、"Pro" と "gra" と "m" というようになります。このうち最初の二つの "Pro" と "gra" については、今までと変わりないので特には触れません。
今までと違うのが "m" です。
| 文字
| m
| | |
| ASCII 値
| 0x6D
| | |
| 8 bit
| 01
| 10
| 11
| 01
| 00
| 00
|
|
|
|
|
|
|
| 6 bit
| 01
| 10
| 11
| 01
| 00
| 00
| | | | | | |
| 値
| 0x1B
| 0x10
| | |
| 64進数
| b
| Q
| =
| =
|
"m" を文字コードに変換すると 8 bit の値ができます。
それ以外にはもう文字がないので、これを 6 bit に分けると、011011 という 6 bit と、あまりの 01 という 2 bit が出てきます。先頭の 6 bit の方はいいのですが、残りの 2 bit を文字に置き換えるには 4 bit たりません。
このような場合、0x00 が次の文字に続いていていたような感じで、不足部分は全て 0 で埋めます。
"m" の場合は端数が 01 です。その次の文字が 0x00 であったと仮定すれば、次に続く 4 bit は 0000 となります。よってこれらを合わせて、2つ目の 6 bit は 010000 というようになります。
これで "m" という字は bQ というように変換できるのですが、Base64 は、その結果は 4 文字ずつでなくてはいけません。
このように、4 文字に満たない文字列に変換される場合は、不足部分を "=" で埋めます。よって、"bQ==" となり、4 文字に変換することができました。
なお、"Pro" は "UHJv" に、そして "gra" は "Z3Jh" に変換されますので、結果として、"Program" は "UHJvZ3JhbQ==" となります。
□ デコード
デコード時は、改行文字や、上記の文字以外は無視する必要があるようです。
が、ちゃんとエンコードされたものを扱う場合、そのような例外的なデータは発生しないでしょうから、改行文字以外で予期しない文字が現れた場合には、何らかの措置をとった方がよさそうです。
とりあえずそれらは抜きにして、デーコードの手順を考えて見ますと、次のような感じでしょうか。
とりあえず、Base64 では 4 文字単位で変換されていますので、戻す際には先頭から 4 文字ずつを取り出して処理を行います。
エンコード済み文字列の 4 バイトごとに、それらを Base64 の 64 進数表から対応する 6 ビットの値に変換して、それらを連結して、8 ビット 3 つの束として取り出せば完了です。
注意点としては、"=" までも復元してしまわないように気をつけるといったところでしょうか。
"=" が現れるのは行末で、最大 2 つまで。2 つ出てきた場合にはそのデータからは 1 つのデータしか取り出せず、1 つだけ出てきた場合はそのデータからは 2 つのデータが取り出せて、1 つも "=" が出てこない場合には 3 つの全てのデータが取り出せるといった感じです。