¿Cómo lograr la codificación segura de URL Base64 en C #?

104

Quiero lograr una codificación segura de URL Base64 en C #. En Java, tenemos la Codecbiblioteca común que me da una cadena codificada segura para URL. ¿Cómo puedo lograr lo mismo usando C #?

byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes("StringToEncode");
string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);

El código anterior lo convierte a Base64, pero rellena ==. ¿Hay alguna forma de lograr una codificación segura de URL?

Vishvesh Phadnis
fuente
¿No puedes usar una Url.Encodecuerda BASE64?
¿En qué espacio de nombres está presente la clase de URL en c #?
Vishvesh Phadnis
Eche un vistazo: msdn.microsoft.com/en-us/library/… Necesita hacer referencia al System.Webensamblado.
Se está convirtiendo = en% 3D. No quiero eso.
Vishvesh Phadnis
3
Entonces, ¿qué quieres decir con url safe? %3Des URL segura.

Respuestas:

181

Es común simplemente intercambiar el alfabeto para usarlo en las URL, por lo que no es necesaria la codificación%; sólo 3 de los 65 caracteres son problemáticos - +, /y =. los reemplazos más comunes están -en lugar de +y _en lugar de /. En cuanto al acolchado: simplemente quítelo (el =); puede inferir la cantidad de relleno necesaria. En el otro extremo: simplemente invierta el proceso:

string returnValue = System.Convert.ToBase64String(toEncodeAsBytes)
        .TrimEnd(padding).Replace('+', '-').Replace('/', '_');

con:

static readonly char[] padding = { '=' };

y para revertir:

string incoming = returnValue
    .Replace('_', '/').Replace('-', '+');
switch(returnValue.Length % 4) {
    case 2: incoming += "=="; break;
    case 3: incoming += "="; break;
}
byte[] bytes = Convert.FromBase64String(incoming);
string originalText = Encoding.ASCII.GetString(bytes);

Sin embargo, la pregunta interesante es: ¿ es este el mismo enfoque que utiliza la "biblioteca de códecs común"? Sin duda, sería una primera cosa razonable para probar; este es un enfoque bastante común.

Marc Gravell
fuente
1
En Common Codec están usando [0-9a-zA-Z_-] Character para el modo seguro de URL.
Vishvesh Phadnis
esto también se menciona en la página wiki de Base64 en aplicaciones URL. en.wikipedia.org/wiki/Base64
wonster
2
También tiene esta función ' stackoverflow.com/questions/1886686/… ' que hace todo el trabajo duro por usted.
toy4fun
1
Por qué no lo necesitamos: caso 1: entrante + = "==="; descanso; ?
alex da franca
5
@alexdafranca porque nunca tendrá una longitud mod 4 de 1. 3x8 bits se convierten en 4x6 bits (y cada 6 bits es uno de los 64 caracteres en el alfabeto elegido), 0x8 bits se codifica como 0x6 bits sin relleno, 1x8 bits se codifica como 2x6 bits con ==relleno, 2x8 se codifica como 3x6 con =relleno, 3x8 se codifica como 4x6 sin relleno y luego se alinea para que se repita. Nada está codificado en 1x6 bits, por lo que nunca necesita ===relleno.
George Helyar
83

Puede usar la clase Base64UrlEncoderdel espacio de nombres Microsoft.IdentityModel.Tokens.

const string StringToEncode = "He=llo+Wo/rld";

var encodedStr = Base64UrlEncoder.Encode(StringToEncode);
var decodedStr = Base64UrlEncoder.Decode(encodedStr);

if (decodedStr == StringToEncode)
    Console.WriteLine("It works!");
else
    Console.WriteLine("Dangit!");
Jerome
fuente
1
Esto es mucho más limpio que la respuesta aceptada. ¿Algún inconveniente?
taktak004
18
Solo una nota: Microsoft.IdentityModel.Tokens es un paquete NuGet que debe descargarse.
Uber Schnoz
Asumo por el nombre que esto no es multiplataforma. ¿Es ese el caso?
Brandon S.
8

Basándonos en las respuestas aquí con algunas mejoras de rendimiento, hemos publicado una implementación de base64 segura para URL muy fácil de usar para NuGet con el código fuente disponible en GitHub (con licencia MIT).

El uso es tan fácil como

var bytes = Encoding.UTF8.GetBytes("Foo");
var encoded = UrlBase64.Encode(bytes);
var decoded = UrlBase64.Decode(encoded);
Mahmoud Al-Qudsi
fuente
Increíble gracias. Por interés, ¿por qué ha optado por "string".Replaceel encodemétodo, pero un bucle con manual reemplaza al decode?
ᴍᴀᴛᴛ ʙᴀᴋᴇʀ
@ ᴍᴀᴛᴛʙᴀᴋᴇʀ Necesito volver a visitarlo y ejecutar algunos puntos de referencia, pero es porque agregamos a este último, por lo que está representado por una lista de caracteres que se puede aumentar en lugar de una cadena inmutable.
Mahmoud Al-Qudsi
Otra clase con tipo de retorno = cadena en su lugar: github.com/vndevpro/architecture-common/blob/master/…
hazjack
8

Para obtener una codificación tipo base64 segura para URL, pero no "base64url" según RFC4648, utilice System.Web.HttpServerUtility.UrlTokenEncode(bytes)para codificar y System.Web.HttpServerUtility.UrlTokenDecode(bytes)decodificar.

Karanvir Kang
fuente
6
Esto no proporciona una codificación Base64 segura para URL conforme a los estándares de acuerdo con RFC4648. Consulte también estas preguntas y respuestas . Úselo con precaución.
Artjom B.
1

Solución más simple: (sin relleno)

private static string Base64UrlEncode(string input) {
    var inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
    // Special "url-safe" base64 encode.
    return Convert.ToBase64String(inputBytes)
      .Replace('+', '-') // replace URL unsafe characters with safe ones
      .Replace('/', '_') // replace URL unsafe characters with safe ones
      .Replace("=", ""); // no padding
  }

El crédito es para: Tholle

Ashi
fuente
0

Aquí hay otro método para decodificar una base64 segura para URL que se codificó de la misma manera con Marc. Simplemente no entiendo por qué 4-length%4funcionó (lo hace).

De la siguiente manera, solo la longitud de bits del origen es un múltiplo común de 6 y 8, base64 no agrega "=" al resultado.

1 2 3 4 5 6 7 8|1 2 3 4 5 6 7 8|1 2 3 4 5 6 7 8 
1 2 3 4 5 6|1 2 3 4 5 6|1 2 3 4 5 6|1 2 3 4 5 6
                "=="            "="

Entonces podemos hacerlo a la inversa, si la longitud de bits del resultado no puede ser divisible por 8, se ha agregado:

base64String = base64String.Replace("-", "+").Replace("_", "/");
var base64 = Encoding.ASCII.GetBytes(base64String);
var padding = base64.Length * 3 % 4;//(base64.Length*6 % 8)/2
if (padding != 0)
{
    base64String = base64String.PadRight(base64String.Length + padding, '=');
}
return Convert.FromBase64String(base64String);
Joyjy
fuente
0

Usando el motor criptográfico de Microsoft en UWP.

uint length = 32;

IBuffer buffer = CryptographicBuffer.GenerateRandom(length);
string base64Str = CryptographicBuffer.EncodeToBase64String(buffer)
                   // ensure url safe
                   .TrimEnd('=').Replace('+', '-').Replace('/', '_');

return base64Str;
visc
fuente
0

La respuesta de Karanvir Kang es buena y yo voté a favor. Sin embargo, deja un carácter impar al final de la cadena (que indica el número de caracteres de relleno eliminados). Aquí está mi solución.

var bytesToEncode = System.Text.Encoding.UTF8.GetBytes("StringToEncode"); 
var bytesEncodedPadded = HttpServerUtility.UrlTokenEncode(bytesToEncode);
var objectIdBase64 = bytesEncodedPadded.Substring(0, bytesEncodedPadded.Length - 1);
Joshcodes
fuente