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: 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, '=');

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

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
    }

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


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

talvez se você fornecer mais detalhes e comparação com a resposta aceita, você possa obter um voto positivo - obrigado
Mauricio Gracia Gutierrez
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.