Hace un par de semanas me enfrenté a un problema interesante: tenía que migrar un algoritmo de cifrado AES de C# a Go. En la implementación en Go ya teníamos un algoritmo de cifrado AES, pero no era compatible con la implementación en C#, y varias pruebas fallaban porque los resultados no coincidían, principalmente en el último carácter.
El problema era que no tenía el código fuente de la implementación en C#, solo el binario, una DLL que se usaba en el proyecto de .NET.
Intenté obtener el código fuente de la implementación en C#, pero no tuve éxito. Al ser un proyecto antiguo, no había documentación disponible. Afortunadamente, mi jefe fue quien desarrolló esta implementación, pero no recordaba los detalles exactos. Sin embargo, sí sabía que al final del proceso de cifrado AES se utilizaba una función para codificar en base64.
Con esta pista, abrí el proyecto en .NET e instalé la extensión de JetBrains para descompilar el código fuente, y obtuve el código de la librería que se usaba para cifrar la información.
Finalmente, descubrí que el problema no residía en el algoritmo de cifrado AES, sino en la codificación en base64.
En el código de C#, al final del proceso de cifrado AES, se usaba la siguiente función para la codificación en base64: HttpServerUtility.UrlTokenEncode
.
La función UrlTokenEncode
es una función de .NET que codifica una matriz de bytes en una cadena de texto base64 para su transmisión en una URL. Esta función realiza tres acciones clave que explican la diferencia en los resultados:
Codificación en Base64 segura para URLs (URL-safe): usa una variante de Base64 que es apta para URLs, reemplazando los caracteres
+
por-
y/
por_
.Eliminación de caracteres de relleno: la función elimina los caracteres de relleno
=
que se suelen utilizar en la codificación estándar de Base64.Adición de un número al final: la función añade un número al final de la cadena para indicar cuántos caracteres de relleno se eliminaron.
Todo esto lo descubrí gracias a ChatGPT, no porque sea un experto en base64. Con esta información, pude modificar la implementación en Go para que fuera compatible con la de C#.
En Go, después de cifrar la información con AES, la codificación en base64 se realiza de la siguiente manera:
encode := base64.RawURLEncoding.EncodeToString(paddedBytes)
Y, finalmente, se añade el número de caracteres de relleno eliminados al final de la cadena:
// Calcular el número de caracteres de relleno (`=`) que se habrían añadido
paddingCount := (4 - len(paddedBytes)%3) % 4
// Añadir el conteo de relleno al final de la cadena codificada (como hace UrlTokenEncode de C#)
if paddingCount > 0 {
encoded += strconv.Itoa(paddingCount)
}
En la línea paddingCount := (4 - len(paddedBytes)%3) % 4
, se calcula el número de caracteres de relleno (=
) que luego se añaden al final de la cadena codificada en base64:
-
len(paddedBytes)%3
calcula la cantidad de bytes que no se han codificado en base64. -
(4 - len(paddedBytes)%3) % 4
calcula cuántos caracteres de relleno faltan para que la longitud sea divisible por 4. Si no se necesita relleno, el resultado es 0.
En resumen, el problema no era el algoritmo de cifrado AES, sino la codificación en base64. Gracias a la información obtenida de ChatGPT, pude modificar la implementación en Go para que fuera compatible con la de C#. En este caso, el uso de ChatGPT fue muy útil, ya que me ahorró un montón de tiempo y dolores de cabeza; eso sí, tuve que ir ajustando cada respuesta hasta igualar los resultados de ambas implementaciones.