Base64 es un esquema de codificación que permite representar datos binarios en un formato de texto ASCII.
Esto es útil cuando necesitamos transmitir datos binarios a través de protocolos que solo admiten texto (por ejemplo, correos electrónicos o URLs).
Características de Base64
- Representación de datos binarios: Base64 convierte datos binarios en texto, lo que facilita su transmisión y almacenamiento.
- Tamaño aumentado: Los datos codificados en Base64 ocupan aproximadamente un 33% más de espacio que los datos originales.
- Uso común: Se utiliza en aplicaciones como correos electrónicos (adjuntos), URLs, autenticación HTTP y almacenamiento de datos.
El sistema Base64 también está relacionado con aplicaciones de seguridad. Muchas veces interviene como una parte del proceso de encriptación o autentificación.
Sin embargo, pero en si mismo no es un método de encriptación. Los datos codificados en Base64 pueden ser fácilmente decodificados.
Si necesitas proteger la confidencialidad de los datos, debes utilizar métodos de encriptación como AES o RSA.
¿Cómo funciona Base64?
La codificación Base64 utiliza un conjunto de 64 caracteres (de ahí su nombre), elegidos por ser son comunes en la mayoría de los sistemas.
Estos caracteres son letras mayúsculas (A-Z), letras minúsculas (a-z), números (0-9) y dos caracteres especiales, generalmente ”+” y ”/“.
Cada uno de estos caracteres puede codificar 6bits de información (2^6 = 64). Pero nuestra información original estaba codificada en múltiplos de 8bits.
Para realizar la conversión,
- Dividimos los datos binarios en bloques de 3 bytes (3 x 8 = 24 bits)
- Convertimos los bloques en 4 caracteres ASCII (4 x 6 = 24 bits).
Pero no siempre la información que queramos codificar va a ser un múltiplo de 3 bytes. Al final de la secuencia, nos pueden sobrar 1 o 2 bytes.
Así que se utiliza el carácter ”=” como relleno.
- Si sobra 1 byte, se añaden 2 caracteres =.
- Si sobran 2 bytes, se añade 1 carácter =.
- Si la cantidad de bytes es un múltiplo de 3, no es necesario padding.
Ejemplo de codificación Base64
Vamos a verlo con un ejemplo. Imaginemos que tenemos estos datos binarios que queremos enviar.
01001000 01101111 01101100 01100001
Para pasarlos a Base64 divide estos bytes en bloques de 6 bits:
010010 000110 111101 101100 011000 01
El bloque final lo completo con 0
010010 000110 111101 101100 011000 0100000
Cada bloque de 6 bits se convierte en un carácter Base64:
S G 9 s Y Q
Como solo tengo 6 letras, y necesito un múltiplo de 4, añado dos =
.
SG9sYQ==
Implementación de Base64 en ESP32
El ESP32 cuenta con una biblioteca (base64.h
) que nos permite codificar y decodificar datos en Base64.
Vamos a ver cómo utilizar la librería en un proyecto de ESP32.
#include "stdlib.h"
#include "mbedtls/base64.h"
void printBlockAsUtf8(std::vector<uint8_t> data)
{
for(int i = 0; i < data.size(); i++)
{
Serial.print((char)data[i]);
}
}
std::string encode_base64(std::vector<uint8_t>& data)
{
size_t outputLength;
mbedtls_base64_encode(nullptr, 0, &outputLength, (unsigned char*)data.data(), data.size());
std::vector<uint8_t> encoded(outputLength);
mbedtls_base64_encode(encoded.data(), outputLength, &outputLength, (unsigned char*)data.data(), data.size());
std::string rst((char*)encoded.data(), outputLength);
return rst;
}
std::vector<uint8_t> decode_base64(std::string& text)
{
size_t outputLength;
mbedtls_base64_decode(nullptr, 0, &outputLength, (unsigned char*)text.c_str(), text.length());
std::vector<uint8_t> decoded(outputLength);
mbedtls_base64_decode(decoded.data(), outputLength, &outputLength, (unsigned char*)text.c_str(), text.length());
return decoded;
}
void setup()
{
Serial.begin(115200);
delay(2000);
std::string message = "www.luisllamas.es";
std::vector<uint8_t> data(message.begin(), message.end());
auto encoded = encode_base64(data);
auto decoded = decode_base64(encoded);
Serial.printf("Original (%d bytes): ", data.size());
printBlockAsUtf8(data);
Serial.println();
Serial.printf("Encoded (%d bytes): ", encoded.length());
Serial.print(encoded.c_str());
Serial.println();
Serial.printf("Decoded (%d bytes): ", decoded.size());
printBlockAsUtf8(decoded);
}
void loop()
{
delay(1000);
}
Y si lo ejecutamos veréis algo como esto
Ejemplos prácticos
Transmisión de datos binarios
Base64 es útil cuando necesitamos transmitir datos binarios a través de protocolos que solo admiten texto, como HTTP o MQTT. Por ejemplo, podemos codificar una imagen en Base64 y enviarla a través de una solicitud HTTP.
#include <WiFi.h>
#include <HTTPClient.h>
#include <base64.h>
const char* ssid = "tu_SSID";
const char* password = "tu_CONTRASEÑA";
void setup() {
Serial.begin(115200);
// Conectar a WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Conectando a WiFi...");
}
Serial.println("Conectado a WiFi");
// Leer imagen desde el sistema de archivos
File file = SPIFFS.open("/imagen.jpg", "r");
if (!file) {
Serial.println("Error al abrir la imagen");
return;
}
// Leer datos de la imagen
size_t fileSize = file.size();
uint8_t* buffer = (uint8_t*)malloc(fileSize);
file.read(buffer, fileSize);
file.close();
// Codificar imagen en Base64
String imagenCodificada = base64::encode(buffer, fileSize);
free(buffer);
// Enviar imagen codificada a través de HTTP
HTTPClient http;
http.begin("http://tuservidor.com/subir-imagen");
http.addHeader("Content-Type", "application/json");
String jsonPayload = "{\"imagen\": \"" + imagenCodificada + "\"}";
int httpResponseCode = http.POST(jsonPayload);
if (httpResponseCode > 0) {
Serial.println("Imagen enviada correctamente");
} else {
Serial.println("Error al enviar la imagen");
}
http.end();
}
void loop() {
// No es necesario hacer nada en el loop
}
Almacenamiento de datos en formato texto
Base64 también es útil para almacenar datos binarios en formatos de texto, como JSON o XML. Por ejemplo, podemos almacenar una firma digital en Base64 en un archivo JSON.
#include <SPIFFS.h>
#include <base64.h>
void setup() {
Serial.begin(115200);
// Inicializar SPIFFS
if (!SPIFFS.begin(true)) {
Serial.println("Error al inicializar SPIFFS");
return;
}
// Generar firma digital (simulada)
uint8_t firma[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
size_t firmaSize = sizeof(firma);
// Codificar firma en Base64
String firmaCodificada = base64::encode(firma, firmaSize);
// Crear archivo JSON con la firma
File file = SPIFFS.open("/firma.json", "w");
if (!file) {
Serial.println("Error al crear el archivo JSON");
return;
}
String jsonContent = "{\"firma\": \"" + firmaCodificada + "\"}";
file.print(jsonContent);
file.close();
Serial.println("Firma almacenada correctamente");
}
void loop() {
// No es necesario hacer nada en el loop
}