Base64: Cómo funciona y lo que deberías saber

Base64: Cómo funciona y lo que deberías saber

Ayer me preguntaron que era Base64 y cómo funcionaba, ni corto ni perezoso le envié un link a esa persona con esas funciones medio ofuscadas de Base64 que no todo el mundo entiende, en lo personal cuando aprendí que era Base64 no supe porque ese código funcionaba y uno se olvida de esas pequeñeses que en realidad no son tan pequeñas.

Base64 es simplemente un cambio de código estándar, de el usado comúnmente por nosotros que son bytes osease 8 bits. a uno en el que usemos 6 bits. ¿Por qué se llama Base64? pues porque:

2^6=64

A que lo veías venir 😛 bueno lo que hacemos es recorrer los bytes y agrupar sus bits en 6-tuplas para procesarlas y convertirlas al alfabeto de 64 posiciones. Una forma muy inmediata de lograr esto es con la siguiente función en Java:

public static String charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
public static String ToBin(int a,int b){
String tmp = "";
do{
tmp = (a&1)+tmp;
}while((a>>=1)>0);
while(tmp.length()%b != 0)tmp="0"+tmp;
return tmp;
}
public static int ToDec(String a){
int b = 0;
for(int n = a.length()-1,c=0;n>-1;n--,c++)
b+=(a.charAt(n)=='1')?(1<<c):0;
return b;
}
public static String Cod(String a){
String tmp = "",tmp2 = "";
for(int n = 0;n<a.length();n++)
tmp+=ToBin((int)a.charAt(n),8);
while(tmp.length()%6 != 0)
tmp+="0";
for(int n = 0;(n+6)<=tmp.length();n+=6)
tmp2+=charset.charAt(ToDec(tmp.substring(n,n+6)));
return tmp2;
}

Y es fácilmente entendible, si vemos bien expresamos cada caracter en binario con 8 posiciones, después hacemos un padding para que la longitud sea congruente con 0 modulo 6. Y cogemos las 6 tuplas que hayan. Las representamos en binario y posteriormente en el charset. El problema es que estamos llevando un problema de byte a byte a un problema con cadenas donde hay a los menos 8 bytes por cada representación binaria, por lo cual no es optimo, lo que nos lleva a un programa mucho mas practico. Pero un poco más complicado de entender:

public static String Code(String a){
String res ="";
int p=0;
for(int n =0,c=0;n<a.length();n++,c++){
switch(c&3){
case 0:
res+=charset.charAt((a.charAt(n)>>2)^p);
p = (a.charAt(n)&3)<<4;
break;
case 1:
res+=charset.charAt((a.charAt(n)>>4)^p);
p = (a.charAt(n)&15)<<2;
break;
case 2:
res+=charset.charAt((a.charAt(n)>>6)^p);
p = (a.charAt(n)&63)<<0;
break;
case 3:
res+=charset.charAt((a.charAt(n)>>8)^p);
p=0;
n--;
break;
}
}
res+=charset.charAt(p);
return res;
}

Ahora la vaina ya esta un poco mas enredada, pero nos damos cuenta que ya no usamos cadenas..Veamos como funciona esto, lo primero que nos damos cuenta es que estamos considerando 4 casos ¿por qué cuatro casos?:

4*6equiv 0 pmod{8}

(si no están familiarizados con esta notación, se llama congruencia, significa que el residuo de 6*4 en la división por 8 es cero). Ahora, consideremos la siguiente tupla de números binarios.

a_1a_2a_3....a_{n-1}a_n

con

(forall i in [1,n])(a_i in mathbb{Z}_2)

y

n equiv 0 pmod{6}

Que n sea equivalente a 0 modulo 6 nos indica que ya hicimos el padding. Ahora, en un principio cada 8-tuplas de números binarios representan a un carácter que vamos a codificar por lo cual, hay otro subindice de forma que (abusando un poco de la notación)

a_{1_1}a_{2_1}a_{3_1}a_{4_1}a_{5_1}a_{6_1}a_{7_1}a_{8_1}a_{9_2}....a_{{n-1}_{lfloor (n-1)/8rfloor}+1}a_{n_{lfloor n/8 rfloor +1}}

Por lo cual, nuestro primer caracter (que el lector debe notar que es congruente con 0 modulo 4 en el código) va a ser:

a_{1_1}a_{2_1}a_{3_1}a_{4_1}a_{5_1}a_{6_1}

Que sugiere un desplazamiento de bytes dos posiciones a la derecha de el original:

a_{1_1}a_{2_1}a_{3_1}a_{4_1}a_{5_1}a_{6_1}a_{7_1}a_{8_1} >> 2

Cuya función desplazamiento se simboliza >> en Java. Pero como sabemos, ese desplazamiento va a ser similar para todos los a_n tales que su j-avo indice cumple la siguiente propiedad:

j equiv 1 pmod{4}

donde

a_{n_j}

j indica en byte a el que pertenece…

Ahora que vimos el primer caso veamos el segundo. Vemos que la segunda 6-tupla resultante será:

a_{7_1}a_{8_1}a_{9_2}a_{10_2}a_{11_2}a_{12_2}

Que obedece a los dos últimos bits de la primera 8 tupla y los primeros 4 de la segunda 8-tupla. Pero tenemos que hacer un arreglo para poder sumar cada entrada de la tupla, le tenemos que desplazar los últimos bits a la primera 8-tupla hacia la izquierda y tenemos que desplazar los primeros 4 bits de la segunda 8-tupla 4 hacia la derecha. Para poder sumar coordenada a coordenada, como se define en las tuplas y es exactamente el desplazamiento que se ve en el código. Y Ese procedimiento se repite con las tuplas que su j-avo indice tenga la propiedad de:

j equiv 2 pmod{4}

Para la tercera tupla sabemos que quedara de la forma

a_{13_2}a_{14_2}a_{15_2}a_{16_2}a_{17_3}a_{18_3}

por lo que sera un desplazamiento de el j-avo byte y pues tendremos también un desplazamiento de la tupla anterior de la cual nos sobraron 4.

Si notan, la siguiente tupla sera toda de el indice j=3…Por lo que la siguiente tupla nos llevara a el mismo procedimiento de el caso 1.

Ahora que podemos ver maso menos como funciona el algoritmo, hay algo mas que debemos ver y es que esos casos se pueden manejar y volver un solo caso viendo que hay una expresión general para cada caso.

public static String Codef(String a){
String res ="";
int p=0;
for(int n =0,c=0;n<a.length();n++,c=(c+1)&3){
res+=charset.charAt((a.charAt(n)>>(c*2+2))^p);
p = ((c!=3)?((a.charAt(n)&((1<<(c*2+2))-1))<<(4-c*2)):0);
n = (c==3)?n-1:n;
}
res+=charset.charAt(p);
return res;
}

Para tener un uso mas cómodo de este algoritmo en Java podemos recurrir a sus librerías ya incluidas

Codificando


String palabra="RicTeAmO"

String palabra_codificada =new sun.misc.BASE64Encoder().encode (palabra.getBytes());

System.out.println(palabra_codificada)

Decodificando


String palabra= null;// creamos un string vacio
sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();// esta variable es la que nos permite pasar de base64

byte[] decodedStr = decoder.decodeBuffer(palabra);

System.out.println(new String(decodedStr)+" ");

Esto quiere decir que no tendremos que hacer ningún import para poder utilizarlas. Espero que lo hayan disfrutado, cualquier pregunta es bien recibida.

5 Comentarios

  1. Responder

    Gracias por la iniciativa viejo radical. arica alguien sabe como instalar el Latex en el blog???

  2. Responder

    Hi

    Muy buen articulo lo que no entendí es por que se arranca en ‘a’ y no en {0 0}a?

    Gracias

  3. Responder

    Bro, la verdad no entiendo la pregunta. Por que no te pasas por aca

    http://foro.redinfocol.org/criptografia/base64-lo-que-deberias-saber/

    que tenemos el mismo articulo, pero con el LaTeX, para que nos entendamos mejor. 🙂

    Gracias por el interes.

    Phicar

  4. Responder

    Hi again

    Excelente, no sabia que lo tenían en latex, la presentación es mucho mejor. Ahora tengo otras dudas.

    Con respecto al charset, es de 64 por que el cifrado es base 64? y por que los últimos caracteres son + y / y no algún otros?

    Aquí a_{1_1}a_{2_1}a_{3_1}a_{4_1}a_{5_1}a_{6_1} lo que quiere decir el subindice es indice del carácter y el subsubindice (que seria el j) es para indicar la 8-tupla a la que pertenece y este se conserva luego de modificar para que quede 6-tupla o cambia para que quede indicando la 6-tupla a la que pertenece?

    Gracias de antemano por responder.

    Happy night

  5. Responder

    Sip, la idea es que sea de 64 porque la base es 64 ;).

    Los ultimos caracteres por estandar, no hay ningun misterio en que sean ellos.

    Sip, el sub(sub) indice es para la tupla a la que pertenece, y no, no cambia para 6-tuplas. Se mantiene con 8-tuplas. Para poder entender el porque de el codigo ;).

    Saludos.

Deja un comentario

Your email address will not be published. Required fields are marked *

You may use these <abbr title="HyperText Markup Language">HTML</abbr> tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>