Como obter a codificação segura de URL Base64 em C #?


105

Desejo alcançar a codificação segura de URL Base64 em C #. Em Java, temos a Codecbiblioteca comum que me fornece uma string codificada de URL segura. Como posso conseguir o mesmo usando C #?

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

O código acima o converte em Base64, mas ele preenche ==. Existe uma maneira de obter uma codificação segura de URL?


Você não pode simplesmente usar Url.Encodena corda BASE64?

Em que classe de URL de namespace está presente em c #?
Vishvesh Phadnis,

Dê uma olhada: msdn.microsoft.com/en-us/library/… Você precisa fazer referência ao System.Webassembly.

Está convertendo = em% 3D. Eu não quero isso.
Vishvesh Phadnis

3
Então, o que você quer dizer url safe? %3Dé url seguro.

Respostas:


182

É comum simplesmente trocar o alfabeto para uso em urls, de forma que nenhuma codificação% seja necessária; apenas 3 dos 65 caracteres são problemáticos - +, /e =. as substituições mais comuns estão -no lugar de +e _no lugar de /. Quanto ao preenchimento: basta retirá-lo (o =); você pode inferir a quantidade de preenchimento necessária. Na outra ponta: basta inverter o processo:

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

com:

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

e para reverter:

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);

A questão interessante, entretanto, é: esta é a mesma abordagem que a "biblioteca de codecs comuns" usa? Certamente seria uma primeira coisa razoável a testar - esta é uma abordagem bastante comum.


1
No Codec Comum, eles estão usando o caractere [0-9a-zA-Z_-] para o modo de segurança de url.
Vishvesh Phadnis

isso também é mencionado na página wiki para Base64 em aplicativos de URL. en.wikipedia.org/wiki/Base64
wonster

2
Você também tem esta função ' stackoverflow.com/questions/1886686/… ' que faz todo o trabalho duro para você.
toy4fun

1
Por que não precisamos: caso 1: entrada + = "==="; pausa; ?
alex da franca

5
@alexdafranca porque nunca terá um mod de comprimento 4 de 1. 3x8 bits tornam-se 4x6 bits (e cada 6 bits é um de 64 caracteres no alfabeto escolhido), 0x8 bits é codificado como 0x6 bits sem preenchimento, 1x8 bits é codificado como 2x6 bits com ==preenchimento, 2x8 é codificado como 3x6 com =preenchimento, 3x8 é codificado como 4x6 sem preenchimento e, em seguida, é alinhado para que se repita. Nada é codificado em 1x6 bits, então você nunca precisa de ===preenchimento.
George Helyar

83

Você pode usar a classe Base64UrlEncoderdo namespace 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!");

1
Isso é muito mais claro do que a resposta aceita. Alguma desvantagem?
taktak004

18
Apenas uma observação: Microsoft.IdentityModel.Tokens é um pacote NuGet que deve ser baixado.
Uber Schnoz de

Estou supondo pelo nome que isso não é plataforma cruzada. É esse o caso?
Brandon S.


8

Com base nas respostas aqui com algumas melhorias de desempenho, publicamos uma implementação base64 segura para url muito fácil de usar no NuGet com o código-fonte disponível no GitHub (licenciado pelo MIT).

O uso é tão fácil quanto

var bytes = Encoding.UTF8.GetBytes("Foo");
var encoded = UrlBase64.Encode(bytes);
var decoded = UrlBase64.Decode(encoded);

Muito obrigado. Fora de interesse por que você optou por "string".Replacepara o encodemétodo, mas um laço com substitui manuais para o decode?
ᴍᴀᴛᴛ ʙᴀᴋᴇʀ

@ ᴍᴀᴛᴛʙᴀᴋᴇʀ Preciso revisitá-lo e executar alguns benchmarks, mas é porque adicionamos ao último, então ele é representado por uma lista de caracteres que pode ser aumentada em vez de uma string imutável.
Mahmoud Al-Qudsi

Outra classe com return type = string ao invés: github.com/vndevpro/architecture-common/blob/master/…
hazjack

8

Para obter uma codificação semelhante a base64 segura para URL, mas não "base64url" de acordo com RFC4648, use System.Web.HttpServerUtility.UrlTokenEncode(bytes)para codificar e System.Web.HttpServerUtility.UrlTokenDecode(bytes)para decodificar.


6
Isso não fornece uma codificação Base64 segura de URL em conformidade com os padrões de acordo com RFC4648. Veja também este Q&A . Use com cuidado.
Artjom B.

1

Solução mais simples: (sem preenchimento)

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
  }

O crédito vai para: Tholle


0

Aqui está outro método para decodificar uma base64 segura para url foi codificada da mesma maneira com Marc. Eu simplesmente não entendo por que 4-length%4funcionou (ele faz).

Como segue, apenas o comprimento de bit da origem são múltiplos comuns de 6 e 8, base64 não acrescenta "=" ao 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
                "=="            "="

Portanto, podemos fazer isso inversamente, se o comprimento de bits do resultado não puder ser divisível por 8, ele foi anexado:

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);

0

Usando o mecanismo criptográfico da Microsoft em UWP.

uint length = 32;

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

return base64Str;

0

A resposta de Karanvir Kang é boa e eu votei a favor. No entanto, ele deixa um caractere ímpar no final da string (indicando o número de caracteres de preenchimento removidos). Aqui está minha solução.

var bytesToEncode = System.Text.Encoding.UTF8.GetBytes("StringToEncode"); 
var bytesEncodedPadded = HttpServerUtility.UrlTokenEncode(bytesToEncode);
var objectIdBase64 = bytesEncodedPadded.Substring(0, bytesEncodedPadded.Length - 1);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.