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?

Vishvesh Phadnis
fonte
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.

Marc Gravell
fonte
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!");
Jerome
fonte
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);
Mahmoud Al-Qudsi
fonte
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.

Karanvir Kang
fonte
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

Ashi
fonte
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);
Joyjy
fonte
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;
visc
fonte
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);
Joshcodes
fonte