Code pour décoder / encoder une URL base64 modifiée

113

Je veux encoder des données en base64 pour les mettre dans une URL, puis les décoder dans mon HttpHandler.

J'ai trouvé que l' encodage Base64 permet un caractère «/» qui va gâcher ma correspondance UriTemplate. Ensuite, j'ai trouvé qu'il existe un concept de "Base64 modifiée pour l'URL" de wikipedia:

Une variante Base64 modifiée pour l'URL existe, où aucun remplissage '=' ne sera utilisé, et les caractères '+' et '/' de la norme Base64 sont respectivement remplacés par '-' et '_', de sorte que l'utilisation des encodeurs / décodeurs d'URL n'est plus nécessaire et n'a aucun impact sur la longueur de la valeur codée, laissant la même forme codée intacte pour une utilisation dans les bases de données relationnelles, les formulaires Web et les identificateurs d'objets en général.

En utilisant .NET Je souhaite modifier mon code actuel, passant de l'encodage et du décodage base64 à l'utilisation de la méthode "base64 modifiée pour l'URL". Quelqu'un at-il fait ça?

Pour décoder, je sais que cela commence par quelque chose comme:

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

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

// My normal base64 decoding now uses encodedText

Mais, j'ai besoin d'ajouter potentiellement un ou deux caractères '=' à la fin, ce qui semble un peu plus complexe.

Ma logique d'encodage devrait être un peu plus simple:

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

J'ai vu le Guid to Base64 pour l' entrée URL StackOverflow, mais cela a une longueur connue et ils peuvent donc coder en dur le nombre de signes égaux nécessaires à la fin.

Kirk Liemohn
la source
@Kirk: Ajustez ma réponse avec des mathématiques testées.
AnthonyWJones

Réponses:

69

Cela devrait le compléter correctement: -

 base64 = base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=');
AnthonyWJones
la source
11
Bah, tu m'as battu. Je vais simplement supprimer mon message car il semble presque que je vous ai copié :)
AaronLS
3
Cela ne va-t-il pas ajouter jusqu'à trois caractères '='? Il semble qu'il n'y en aura que 0, 1 ou 2.
Kirk Liemohn
1
@Kirk: s'il ajoute 3 caractères, la chaîne base64 est déjà corrompue. Je suppose que ce serait une bonne idée de valider la chaîne, elle ne devrait contenir que les caractères attendus et Longueur% 4! = 3.
AnthonyWJones
Hmmm. En essayant cela, cela ne fonctionne pas. Toujours à la recherche de réponses. Le nombre de signes égaux ne se déroule tout simplement pas correctement.
Kirk Liemohn
2
@AnthonyWJones 'il ne doit contenir que les caractères attendus et Longueur% 4! = 1 ', non?
blueling le
173

Vérifiez également la classe HttpServerUtility avec les méthodes UrlTokenEncode et UrlTokenDecode qui gèrent le codage et le décodage en Base64 sécurisés pour les URL.

Remarque 1: le résultat n'est pas une chaîne Base64 valide. Certains caractères non sécurisés pour l'URL sont remplacés.

Remarque 2: Le résultat diffère de l'algorithme base64url dans 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
la source
Merci pour le conseil. J'essaierai ça la prochaine fois!
Kirk Liemohn
12
Votre astuce était la fin glorieuse d'une recherche de plusieurs heures et de touffes de cheveux pour la réponse. merci
Praesagus
Est-ce que cela n'utilisera pas le codage% pour chaque / + et =? Ce n'est pas aussi efficace que l'autre réponse
JoelFan
Non, il remplace les signes égaux utilisés pour le remplissage à la fin par un nombre et remplace plus et barre oblique par moins et soulignement.
Fredrik Haglund
15
Notez que ce UrlTokenEncoden'est pas strictement base64url , car il remplace le remplissage '=' par '0', '1' ou '2' selon le nombre de signes égaux qu'il a remplacé.
chargé
28

Pas assez de points pour commenter, mais au cas où cela aiderait, l'extrait de code que Sushil a trouvé dans le lien fourni (JSON Web Signature ietf draft) fonctionne lors de l'encodage de Base 64 en tant que paramètre dans l'URL.

Extrait copié ci-dessous pour ceux qui sont paresseux:

    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
la source
ceci est compatible avec Xamarin pour ne pas utiliser System.Web
Reza Mortazavi
Exactement ce que je cherchais! Très bonne option pour Xamarin sans avoir à extraire une bibliothèque.
Sleeping_Giant
19

J'ai frappé ici en cherchant du code à encoder / décoder pour l'encodage base64url qui est peu différent de base64 comme expliqué dans la question.

Extrait de code c # trouvé dans ce document. Brouillon ietf de signature Web JSON

Sushil
la source
2
C'était la seule solution qui fonctionnait pour moi lors de l'analyse d'un message dans l'API GMail v1 (Message.Raw)
HeyZiko
5

En comparaison avec la réponse acceptée, voici comment décoder fondamentalement une URL encodée en base64, en utilisant C #:

Décoder:

string codedValue = "base64encodedUrlHere";

string decoded;
byte[] buffer =  Convert.FromBase64String(codedValue);
decoded = Encoding.UTF8.GetString(buffer);
Chris Halcrow
la source
peut-être que si vous fournissez plus de détails et une comparaison avec la réponse acceptée, vous pourriez obtenir un vote favorable - merci
Mauricio Gracia Gutierrez