こんにちは、MSKです。
仕事でBase64エンコードを使うことになったので調べました。
Base64とは
Base64はバイナリデータを次に紹介する規則に基づいて文字列に置き換える変換方法です。
64種類の英数字と記号(大文字のAからZ、小文字のaからz、数字の0から9、2種類の記号+と/とパディングとして=)のみを用いて表現されます。
バイナリデータを受け付けることができないシステム(電子メールなど)にバイナリデータを送るために使用されます。
Base64への変換方法
変換方法は以下になります。
- 元データを6bitずつに分割します。(最後のデータは6bitに足りなくてもOK)
- 最後のデータが6bitになっていない場合、右側に0を追加して6bitにします。
- 下の変換表を用いて、4文字ずつ変換します。
- 最後に4文字に足りない場合は=を右側に追加します。
- 4文字ずつ分割されている文字列たちを連結して1つの文字列にします。
- 完成した文字列が変換したデータになります。
変換表
ちょっと見づらいですが、左側のbit列を変換した結果が、その列の右側のbase64の列になります。
bit列 | Base64 | bit列 | Base64 |
---|---|---|---|
000000 | A | 100000 | g |
000001 | B | 100001 | h |
000010 | C | 100010 | i |
000011 | D | 100011 | j |
000100 | E | 100100 | k |
000101 | F | 100101 | l |
000110 | G | 100110 | m |
000111 | H | 100111 | n |
001000 | I | 101000 | o |
001001 | J | 101001 | p |
001010 | K | 101010 | q |
001011 | L | 101011 | r |
001100 | M | 101100 | s |
001101 | N | 101101 | t |
001110 | O | 101110 | u |
001111 | P | 101111 | v |
010000 | Q | 110000 | w |
010001 | R | 110001 | x |
010010 | S | 110010 | y |
010011 | T | 110011 | z |
010100 | U | 110100 | 0 |
010101 | V | 110101 | 1 |
010110 | W | 110110 | 2 |
010111 | X | 110111 | 3 |
011000 | Y | 111000 | 4 |
011001 | Z | 111001 | 5 |
011010 | a | 111010 | 6 |
011011 | b | 111011 | 7 |
011100 | c | 111100 | 8 |
011101 | d | 111101 | 9 |
011110 | e | 111110 | + |
011111 | f | 111111 | / |
いくつかBase64にエンコード・デコードしてみる
試しにいくつかのバイナリや文字列をBase64にエンコードして、デコードしてみます。
バイナリ列のエンコード
まず、バイナリ列からエンコードしてみます。
[0x01,0xA0,0x9B,0x35]というバイト列をエンコードしていきます。
まず上のバイト列を2進数に直します。
0x01 = 0b00000001
0xA0 = 0b10100000
0x9B = 0b10011011
0x35 = 0b00110101
次に上から順番に6bit毎に区切っていきます。
0b000000
0b011010
0b000010
0b011011
0b001101
0b01
最後が4bit足りないので、0で埋めます。
0b000000
0b011010
0b000010
0b011011
0b001101
0b010000
次に変換表から6bitのバイナリデータを4文字ずつ文字に変換します。
0b000000 = A
0b011010 = a
0b000010 = C
0b011011 = b
次が4文字に足りないので、最後に=を2つ追加します。
0b001101 = N
0b010000 = Q
=
=
できた文字を連結します。
AaCbNQ==
これが[0x01,0xA0,0x9B,0x35]をエンコードしたデータになります。
デコードは逆の手順をたどればできるので、省略します。
文字列のエンコード
次に「Hello,World!」という文字列をエンコードしてみます。
まず、Hello,World!という文字列をバイナリに直します。
[0x48,0x65,0x6C,0x6C,0x6F,0x2C,0x57,0x6F,0x72,0x6C,0x64,0x21]となります。
まずは2進数に変換します。
0x48 = 0b01001000
0x65 = 0b01100101
0x6C = 0b01101100
0x6C = 0b01101100
0x6F = 0b01101111
0x2C = 0b00101100
0x57 = 0b01010111
0x6F = 0b01101111
0x72 = 0b01110010
0x6C = 0b01101100
0x64 = 0b01100100
0x21 = 0b00100001
これを同じように6bitごとに分割します。
0b010010
0b000110
0b010101
0b101100
0b011011
0b000110
0b111100
0b101100
0b010101
0b110110
0b111101
0b110010
0b011011
0b000110
0b010000
0b100001
次に変換表から6bitのバイナリデータを4文字ずつ文字に変換します。
0b010010 = S
0b000110 = G
0b010101 = V
0b101100 = s
0b011011 = b
0b000110 = G
0b111100 = 8
0b101100 = s
0b010101 = V
0b110110 = 2
0b111101 = 9
0b110010 = y
0b011011 = b
0b000110 = G
0b010000 = Q
0b100001 = h
できた文字を連結します。
SGVsbG8sV29ybGQh
これが「Hello,World!」をエンコードしたデータになります。
Rustでエンコード・デコード
Rustにはbase64というクレートがあるので、それを使います。
使うためにCargo.tomlに次のように記述します。
[dependencies] base64="0.13.0"
実際のソースは次のようになります。
use base64::{encode,decode}; fn main() { let test_bin:&[u8] = &[0x01,0xA0,0x9B,0x35]; let encode_bin : String = encode(test_bin); println!("{:?} encode {}",test_bin,encode_bin); match decode(&encode_bin) { Ok(v) => println!("{} decode {:?}",encode_bin,v), Err(v) => println!("decode failed:{}",v), } let test_str : String = "Hello,World!".to_string(); let encode_str : String = encode(test_str.as_bytes()); println!("{} encode {}",test_str,encode_str); match decode(&encode_str) { Ok(v) => println!("{} decode {}",encode_str,v.iter().map(|&s| s as char).collect::<String>()), Err(v) => println!("decode failed:{}",v), } }
encodeはバイト列を受け取って、base64にエンコードしてくれます。
デコードの場合はResult型が返されるので、matchで分岐させています。
Pythonでエンコード・デコード
PythonにもBase64を扱うbase64モジュールがあります。
import base64 from encodings import utf_8 test_bin = bytes([0x01,0xA0,0x9B,0x35]) encode_bin = base64.b64encode(test_bin) print(test_bin.hex(":")," encode ",encode_bin.decode()) decode_bin = base64.b64decode(encode_bin) print(encode_bin.decode()," decode ",decode_bin.hex(":")) test_str = "Hello,World!" encode_str = base64.b64encode(test_str.encode()) print(test_str," encode ",encode_str.decode()) decode_str = base64.b64decode(encode_str) print(encode_str.decode()," decode ",decode_str.decode())
Rustと同じようにb64encodeでエンコードし、b64decodeでデコードします。
JavaScriptでエンコード・デコード
最後にJavaScriptでBase64エンコード・デコードを行ってみます。
const test_bin = [0x01,0xA0,0x9B,0x35]; let str_test_bin = "" for(let i =0;i<test_bin.length;i++){ str_test_bin += String.fromCharCode(test_bin[i]); } const encode_bin = btoa(str_test_bin) console.log(test_bin+" encode "+encode_bin) const decode_bin = atob(encode_bin); let decode_bin_array = []; for(let i =0;i<decode_bin.length;i++){ decode_bin_array[i] = decode_bin.charCodeAt(i); } console.log(encode_bin+" decode "+decode_bin_array); const test_str = "Hello,World!" const encode_str = btoa(test_str) console.log(test_str+" encode "+encode_str) const decode_str = atob(encode_str); console.log(encode_str+" decode "+decode_str);
javascriptの場合バイト列をエンコードする場合にめんどくさい処理が必要です。
btoaメソッドでBase64に変換をするのですが、引数がバイナリデータの文字列である必要があります。
String.fromCharCodeにより対象となるバイナリデータを文字コードとする文字列を作り、その値をatobに入れてあげることでBase64にエンコードできます。
最後に
Base64エンコーディングについて調べて、プログラムとして組んでみました。
どのプログラムもある程度簡単に使うことができますね。
以上、「RustとPython、JavaScriptでBase64でエンコード・デコードしてみる!」でした。
最後までご覧頂き、ありがとうございました!