Código para decodificar / codificar um URL base64 modificado

113

Quero codificar os dados em base64 para colocá-los em uma URL e, em seguida, decodificá-los no meu HttpHandler.

Eu descobri que a codificação Base64 permite um caractere '/' que bagunçará a correspondência do meu UriTemplate. Então descobri que existe um conceito de "Base64 modificada para URL" da wikipedia:

Existe uma base64 modificada para a variante de URL, onde nenhum preenchimento '=' será usado, e os caracteres '+' e '/' do Base64 padrão são substituídos respectivamente por '-' e '_', de modo que usando codificadores / decodificadores de URL não é mais necessário e não tem impacto sobre o comprimento do valor codificado, deixando a mesma forma codificada intacta para uso em bancos de dados relacionais, formulários da web e identificadores de objeto em geral.

Usando o .NET, quero modificar meu código atual de codificação e decodificação básicas em base64 para o método "base64 modificada para URL". Alguém já fez isso?

Para decodificar, eu sei que começa com algo como:

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

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

// My normal base64 decoding now uses encodedText

Mas, eu preciso adicionar potencialmente um ou dois caracteres '=' ao final, o que parece um pouco mais complexo.

Minha lógica de codificação deve ser um pouco mais simples:

// 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('/', '_');

Eu vi o Guid para Base64 para a entrada URL StackOverflow, mas isso tem um comprimento conhecido e, portanto, eles podem codificar o número de sinais de igual necessários no final.

Kirk Liemohn
fonte
@Kirk: Ajuste minha resposta com matemática testada.
AnthonyWJones

Respostas:

69

Isso deve preenchê-lo corretamente: -

 base64 = base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=');
AnthonyWJones
fonte
11
Bah, você me bateu. Vou deletar minha postagem porque parece que quase copiei você :)
AaronLS
3
Isso não vai somar três '=' caracteres? Parece que haverá apenas 0, 1 ou 2 desses.
Kirk Liemohn
1
@Kirk: Se adicionar 3 caracteres, a string base64 já está corrompida. Acho que seria uma boa ideia validar a string, ela deve conter apenas os caracteres esperados e Comprimento% 4! = 3.
AnthonyWJones
Hmmm. Ao tentar isso, não está funcionando. Ainda procurando respostas. O número de sinais de igual simplesmente não está funcionando corretamente.
Kirk Liemohn
2
@AnthonyWJones 'deve conter apenas os caracteres esperados e Comprimento% 4! = 1 ', certo?
blueling
173

Verifique também a classe HttpServerUtility com os métodos UrlTokenEncode e UrlTokenDecode que está lidando com a codificação e decodificação Base64 segura de URL.

Nota 1: o resultado não é uma string Base64 válida. Alguns caracteres não seguros para URL são substituídos.

Nota 2: O resultado difere do algoritmo base64url em 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
fonte
Obrigado pela dica. Vou tentar da próxima vez!
Kirk Liemohn
12
Sua dica foi o final glorioso de uma busca de várias horas por tufos de cabelo pela resposta. obrigado
Praesagus
Isso não usará codificação% para cada / + e =? Esta não é tão eficiente quanto a outra resposta
JoelFan 01 de
Não, ele substitui sinais de igual usados ​​para preenchimento no final por um número e substitui mais e barra por menos e sublinhado.
Fredrik Haglund
15
Observe que UrlTokenEncodenão é estritamente base64url , pois substitui o preenchimento '=' por '0', '1' ou '2', dependendo de quantos sinais de igual ele substituiu.
carregado em
28

Não há pontos suficientes para comentar, mas caso ajude, o snippet de código que Sushil encontrou no link fornecido (JSON Web Signature ietf draft) funciona para ao codificar Base 64 como um parâmetro na URL.

Snippet copiado abaixo para aqueles que são preguiçosos:

    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
fonte
isso é compatível com Xamarin por não usar System.Web
Reza Mortazavi
Exatamente o que eu estava procurando! Uma opção realmente boa para o Xamarin sem ter que puxar uma biblioteca.
Sleeping_Giant
19

Bati aqui enquanto procuro o código para fazer a codificação / decodificação para a codificação base64url, que é um pouco diferente da base64, conforme explicado na pergunta.

Snippet de código c # encontrado neste documento. Rascunho de ietf de assinatura da Web JSON

Sushil
fonte
2
Esta foi a única solução que funcionou para mim ao analisar uma mensagem na API v1 do GMail (Message.Raw)
HeyZiko
5

Em comparação com a resposta aceita, aqui está como você decodificaria fundamentalmente um url codificado em base64, usando C #:

Decodificar:

string codedValue = "base64encodedUrlHere";

string decoded;
byte[] buffer =  Convert.FromBase64String(codedValue);
decoded = Encoding.UTF8.GetString(buffer);
Chris Halcrow
fonte
talvez se você fornecer mais detalhes e comparação com a resposta aceita, você possa obter um voto positivo - obrigado
Mauricio Gracia Gutierrez