Base64とはバイナリーデータを扱えない環境において、マルチバイト文字や画像データなどの添付ファイルを扱えるようにするためのエンコード方式です。 その名の「64」が示すとおり、あらゆるデータを64個のASCII文字データと「=」に置き換えます。電子メールやBasic認証などで用いられています。
目次
変換方法
データをBase64エンコードする手順を示します。
- 変換したいデータを6ビットずつのブロックに分割します。
- 最終ブロックが6ビットに満たない場合、下位ビットに「0」を追加して6ビットにします。
- 変換表にしたがってASCIIデータに変換します。
- ASCIIデータ長が4で割り切れない場合、4の倍数になるまで後ろに「=」を追加します。
エンコード例
ASCIIの「ABCDE」を変換するものとします。
- 2進数に置き換えます。
“01000001, 01000010, 01000011, 01000100, 01000101” - 6ビットずつに区切ります。
“010000, 010100, 001001, 000011, 010001, 000100, 0101” - 最終ブロックが6ビットに満たないため、「0」を追加して6ビットにします。
“010000, 010100, 001001, 000011, 010001, 000100, 010100” - 変換表にしたがってASCIIデータに変換します。
“QUJDREU” - ASCIIデータ長が4で割り切れないため、「=」を追加します。
“QUJDREU=”
変換表
スクロールできます
2進 | 10進 | 文字 | 2進 | 10進 | 文字 | |
---|---|---|---|---|---|---|
000000 | 0 | A | 100000 | 32 | g | |
000001 | 1 | B | 100001 | 33 | h | |
000010 | 2 | C | 100010 | 34 | i | |
000011 | 3 | D | 100011 | 35 | j | |
000100 | 4 | E | 100100 | 36 | k | |
000101 | 5 | F | 100101 | 37 | l | |
000110 | 6 | G | 100110 | 38 | m | |
000111 | 7 | H | 100111 | 39 | n | |
001000 | 8 | I | 101000 | 40 | o | |
001001 | 9 | J | 101001 | 41 | p | |
001010 | 10 | K | 101010 | 42 | q | |
001011 | 11 | L | 101011 | 43 | r | |
001100 | 12 | M | 101100 | 44 | s | |
001101 | 13 | N | 101101 | 45 | t | |
001110 | 14 | O | 101110 | 46 | u | |
001111 | 15 | P | 101111 | 47 | v | |
010000 | 16 | Q | 110000 | 48 | w | |
010001 | 17 | R | 110001 | 49 | x | |
010010 | 18 | S | 110010 | 50 | y | |
010011 | 19 | T | 110011 | 51 | z | |
010100 | 20 | U | 110100 | 52 | 0 | |
010101 | 21 | V | 110101 | 53 | 1 | |
010110 | 22 | W | 110110 | 54 | 2 | |
010111 | 23 | X | 110111 | 55 | 3 | |
011000 | 24 | Y | 111000 | 56 | 4 | |
011001 | 25 | Z | 111001 | 57 | 5 | |
011010 | 26 | a | 111010 | 58 | 6 | |
011011 | 27 | b | 111011 | 59 | 7 | |
011100 | 28 | c | 111100 | 60 | 8 | |
011101 | 29 | d | 111101 | 61 | 9 | |
011110 | 30 | e | 111110 | 62 | + | |
011111 | 31 | f | 111111 | 63 | / |
サンプルコード
[base64.c]
/**
* @file base64.c
* @brief Base64処理
*/
//------------------------------------------------------------------------------
// include
//------------------------------------------------------------------------------
#include "base64.h"
//------------------------------------------------------------------------------
// private variable
//------------------------------------------------------------------------------
//! Base64エンコードテーブル
static const uint8_t m_enc_table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// clang-format off
//! @brief Base64デコードテーブル
//! @details '+'~'z'のASCIIデータ
static const int8_t m_dec_table[] = {
62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
// clang-format on
//------------------------------------------------------------------------------
// private function
//------------------------------------------------------------------------------
/** ----------------------------------------------------------------------------
* @brief Base64エンコード
*
* @param [in] bin : バイナリーデータ
* @param bin_size : バイナリーデータ長
* @param [out] ascii : ASCIIデータ
* @return ASCIIデータ長
*/
int32_t base64_encode(const uint8_t bin[], uint32_t bin_size, uint8_t ascii[])
{
uint8_t bit6;
uint8_t buf_size = 0;
uint16_t buf = 0;
uint32_t ascii_size = 0;
for (uint32_t i = 0; i < bin_size; i++) {
buf = (uint16_t)(buf << 8) | bin[i];
buf_size += 8;
// 6ビット毎に変換
do {
bit6 = (uint8_t)(buf >> (buf_size - 6)) & 0x3f;
buf_size -= 6;
ascii[ascii_size] = m_enc_table[bit6];
ascii_size++;
} while (buf_size >= 6);
}
// 端数ビットがある場合は0を補完して変換
if (buf_size > 0) {
bit6 = (uint8_t)(buf << (6 - buf_size)) & 0x3f;
ascii[ascii_size] = m_enc_table[bit6];
ascii_size++;
}
// バイト数が4の倍数でないなら'='を追加
while ((ascii_size % 4) != 0) {
ascii[ascii_size] = (uint8_t)'=';
ascii_size++;
}
ascii[ascii_size] = (uint8_t)'\0';
return (int32_t)ascii_size;
}
/** ----------------------------------------------------------------------------
* @brief Base64デコード
*
* @param [in] ascii : ASCIIデータ
* @param ascii_size : ASCIIデータ長
* @param [out] bin : バイナリーデータ
* @return バイナリーデータ長
* @retval データ長 >= 0 : バイナリデータ長
* @retval データ長 < 0 : ASCIIデータ異常
*/
int32_t base64_decode(const uint8_t ascii[], uint32_t ascii_size, uint8_t bin[])
{
int8_t i;
int8_t tmp;
uint8_t buf[4];
uint32_t size = 0;
uint32_t bin_size = 0;
if ((ascii_size % 4) != 0) {
return -1;
}
do {
// 4バイトのASCIIデータを3バイトのバイナリーデータに変換
for (i = 0; i < 4; i++) {
if ((ascii[size] < (uint8_t)'+') || (ascii[size] > (uint8_t)'z')) {
return -1;
}
tmp = m_dec_table[ascii[size] - (uint8_t)'+'];
if (tmp < 0) {
return -1;
}
buf[i] = (uint8_t)tmp;
size++;
}
bin[bin_size] = (uint8_t)((uint8_t)(buf[0] & 0x3f) << 2) |
(uint8_t)((uint8_t)(buf[1] & 0x30) >> 4);
bin_size++;
bin[bin_size] = (uint8_t)((uint8_t)(buf[1] & 0x0f) << 4) |
(uint8_t)((uint8_t)(buf[2] & 0x3c) >> 2);
bin_size++;
bin[bin_size] = (uint8_t)((uint8_t)(buf[2] & 0x03) << 6) |
(uint8_t)((uint8_t)(buf[3] & 0x3f));
bin_size++;
} while (size < ascii_size);
// '='分を削除
if (ascii[ascii_size - 1] == (uint8_t)'=') {
bin_size--;
}
if (ascii[ascii_size - 2] == (uint8_t)'=') {
bin_size--;
}
return bin_size;
}
[base64.h]
#ifndef BASE64_H
#define BASE64_H
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------------------------------------------------------
// include
//------------------------------------------------------------------------------
#include <stdint.h>
//------------------------------------------------------------------------------
// define
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// function
//------------------------------------------------------------------------------
int32_t base64_encode(const uint8_t bin[], uint32_t bin_size, uint8_t ascii[]);
int32_t base64_decode(const uint8_t ascii[], uint32_t ascii_size,
uint8_t bin[]);
#ifdef __cplusplus
}
#endif
#endif
サンプルコードの使用例
使用例代わりに、CppUTestによるテストコードを掲載しておきます。
#include <string.h>
#include "CppUTest/TestHarness.h"
#include "base64.h"
// clang-format off
TEST_GROUP(base64){
TEST_SETUP(){
}
TEST_TEARDOWN(){
}
};
// clang-format on
TEST(base64, Test_base64_encode)
{
uint8_t bin[128];
uint8_t ascii[128];
int32_t size;
// 成功パターン
strncpy((char *)bin, "ABCDE", 5);
size = base64_encode(bin, 5, ascii);
LONGS_EQUAL(size, 8);
STRNCMP_EQUAL((char *)ascii, "QUJDREU=", size);
}
TEST(base64, Test_base64_decode)
{
uint8_t bin[128];
uint8_t ascii[128];
int32_t size;
// 成功パターン
strncpy((char *)ascii, "QUJDREU=", 8);
size = base64_decode(ascii, 8, bin);
LONGS_EQUAL(size, 5);
STRNCMP_EQUAL((char *)bin, "ABCDE", size);
}
CppUTestでC/C++コードをテストする
CppUTestは、単体テストを自動化するためのフリーソフトウェアです。 C/C++は非常に自由度が高く何でもできる反面、ちょっとしたミスですぐに不具合を起こします。この…