Código para decodificar / codificar una URL base64 modificada

113

Quiero codificar datos en base64 para ponerlos en una URL y luego decodificarlos dentro de mi HttpHandler.

Descubrí que la codificación Base64 permite un carácter '/' que estropeará mi coincidencia de UriTemplate. Luego descubrí que hay un concepto de "Base64 modificada para URL" de wikipedia:

Existe una variante de Base64 modificada para URL, donde no se usará el relleno '=', y los caracteres '+' y '/' de Base64 estándar se reemplazan respectivamente por '-' y '_', de modo que el uso de codificadores / decodificadores de URL ya no es necesario y no tiene ningún impacto en la longitud del valor codificado, dejando la misma forma codificada intacta para su uso en bases de datos relacionales, formularios web e identificadores de objetos en general.

Usando .NET Quiero modificar mi código actual de hacer la codificación y decodificación básica de base64 a usar el método "base64 modificado para URL". ¿Alguien ha hecho esto?

Para decodificar, sé que comienza con algo como:

string base64EncodedText = base64UrlEncodedText.Replace('-', '+').Replace('_', '/');

// Append '=' char(s) if necessary - how best to do this?

// My normal base64 decoding now uses encodedText

Pero, necesito agregar potencialmente uno o dos caracteres '=' al final, lo que parece un poco más complejo.

Mi lógica de codificación debería ser un poco más simple:

// Perform normal base64 encoding
byte[] encodedBytes = Encoding.UTF8.GetBytes(unencodedText);
string base64EncodedText = Convert.ToBase64String(encodedBytes);

// Apply URL variant
string base64UrlEncodedText = base64EncodedText.Replace("=", String.Empty).Replace('+', '-').Replace('/', '_');

He visto el Guid to Base64 para la entrada URL StackOverflow, pero tiene una longitud conocida y, por lo tanto, pueden codificar la cantidad de signos iguales necesarios al final.

Kirk Liemohn
fuente
@ Kirk: Ajusta mi respuesta con matemáticas probadas.
AnthonyWJones

Respuestas:

69

Esto debería rellenarlo correctamente: -

 base64 = base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=');
AnthonyWJones
fuente
11
Bah, me ganaste. Simplemente borraré mi publicación porque parece que te he copiado :)
AaronLS
3
¿No sumará esto hasta tres caracteres '='? Parece que solo habrá 0, 1 o 2 de estos.
Kirk Liemohn
1
@Kirk: Si agrega 3 caracteres, la cadena base64 ya está dañada. Supongo que sería una buena idea validar la cadena, solo debería contener los caracteres esperados y la longitud% 4! = 3.
AnthonyWJones
Hmmm. Al probar esto, no está funcionando. Sigo buscando respuestas. El número de signos iguales simplemente no está funcionando correctamente.
Kirk Liemohn
2
@AnthonyWJones 'solo debe contener los caracteres esperados y la longitud% 4! = 1 ', ¿verdad?
Blueling
173

También verifique la clase HttpServerUtility con los métodos UrlTokenEncode y UrlTokenDecode que manejan la codificación y decodificación Base64 segura para URL.

Nota 1: El resultado no es una cadena Base64 válida. Se reemplazan algunos caracteres no seguros para URL.

Nota 2: El resultado difiere del algoritmo base64url en RFC4648.

///<summary>
/// Base 64 Encoding with URL and Filename Safe Alphabet using UTF-8 character set.
///</summary>
///<param name="str">The origianl string</param>
///<returns>The Base64 encoded string</returns>
public static string Base64ForUrlEncode(string str)
{
    byte[] encbuff = Encoding.UTF8.GetBytes(str);
    return HttpServerUtility.UrlTokenEncode(encbuff);
}
///<summary>
/// Decode Base64 encoded string with URL and Filename Safe Alphabet using UTF-8.
///</summary>
///<param name="str">Base64 code</param>
///<returns>The decoded string.</returns>
public static string Base64ForUrlDecode(string str)
{
    byte[] decbuff = HttpServerUtility.UrlTokenDecode(str);
    return Encoding.UTF8.GetString(decbuff);
}
Fredrik Haglund
fuente
Gracias por el consejo. ¡Lo intentaré la próxima vez!
Kirk Liemohn
12
Tu consejo fue el glorioso final de una búsqueda de varias horas y un mechón de cabello para encontrar la respuesta. gracias
Praesagus
¿No usará codificación% para cada / + y =? Esta no es tan eficiente como la otra respuesta
JoelFan
No, reemplaza los signos iguales utilizados para el relleno al final con un número y sustituye más y barra con menos y guión bajo.
Fredrik Haglund
15
Tenga en cuenta que UrlTokenEncodeno es estrictamente base64url , ya que reemplaza el relleno '=' con '0', '1' o '2' dependiendo de cuántos signos iguales reemplazó.
ladenedge
28

No hay suficientes puntos para comentar, pero en caso de que ayude, el fragmento de código que Sushil encontró en el enlace provisto (JSON Web Signature ietf draft) funciona cuando se codifica Base 64 como parámetro en la URL.

Fragmento copiado a continuación para aquellos que son vagos:

    static string Base64UrlEncode(byte[] arg)
    {
        string s = Convert.ToBase64String(arg); // Regular base64 encoder
        s = s.Split('=')[0]; // Remove any trailing '='s
        s = s.Replace('+', '-'); // 62nd char of encoding
        s = s.Replace('/', '_'); // 63rd char of encoding
        return s;
    }

    static byte[] Base64UrlDecode(string arg)
    {
        string s = arg;
        s = s.Replace('-', '+'); // 62nd char of encoding
        s = s.Replace('_', '/'); // 63rd char of encoding
        switch (s.Length % 4) // Pad with trailing '='s
        {
            case 0: break; // No pad chars in this case
            case 2: s += "=="; break; // Two pad chars
            case 3: s += "="; break; // One pad char
            default: throw new System.Exception(
              "Illegal base64url string!");
        }
        return Convert.FromBase64String(s); // Standard base64 decoder
    }
Stefan Zvonar
fuente
esto es compatible con Xamarin por no usar System.Web
Reza Mortazavi
¡Exactamente lo que estaba buscando! Realmente buena opción para Xamarin sin tener que extraer una biblioteca.
Sleeping_Giant
19

Presioné aquí mientras buscaba código para codificar / decodificar para la codificación base64url, que es un poco diferente a base64 como se explica en la pregunta.

Se encontró un fragmento de código c # en este documento. Borrador de ietf de firma web JSON

Sushil
fuente
2
Esta fue la única solución que funcionó para mí al analizar un mensaje en la API de GMail v1 (Message.Raw)
HeyZiko
5

En comparación con la respuesta aceptada, así es como decodificaría fundamentalmente una URL codificada en base64, usando C #:

Descodificar:

string codedValue = "base64encodedUrlHere";

string decoded;
byte[] buffer =  Convert.FromBase64String(codedValue);
decoded = Encoding.UTF8.GetString(buffer);
Chris Halcrow
fuente
tal vez si proporciona más detalles y una comparación con la respuesta aceptada, podría obtener un voto a favor - gracias
Mauricio Gracia Gutierrez