Kod do dekodowania / kodowania zmodyfikowanego adresu URL base64

113

Chcę kodować dane base64, aby umieścić je w adresie URL, a następnie zdekodować je w moim HttpHandler.

Odkryłem, że kodowanie Base64 umożliwia użycie znaku „/”, który zepsuje dopasowanie mojego UriTemplate. Potem odkryłem, że istnieje koncepcja „zmodyfikowanego Base64 dla adresu URL” z Wikipedii:

Istnieje zmodyfikowany Base64 dla wariantu adresu URL, w którym nie będzie używane dopełnienie „=”, a znaki „+” i „/” standardowego Base64 są odpowiednio zastępowane znakami „-” i „_”, dzięki czemu przy użyciu koderów / dekoderów adresów URL nie jest już konieczne i nie ma wpływu na długość zakodowanej wartości, pozostawiając tę ​​samą zakodowaną postać nienaruszoną do użytku w relacyjnych bazach danych, formularzach internetowych i ogólnie w identyfikatorach obiektów.

Używając .NET Chcę zmodyfikować mój bieżący kod z podstawowego kodowania i dekodowania base64 na metodę „zmodyfikowanego base64 dla adresu URL”. Czy ktoś to zrobił?

Aby zdekodować, wiem, że zaczyna się od czegoś takiego:

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

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

// My normal base64 decoding now uses encodedText

Ale muszę potencjalnie dodać jeden lub dwa znaki „=” na końcu, co wygląda na nieco bardziej złożone.

Moja logika kodowania powinna być trochę prostsza:

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

Widziałem Guid to Base64 dla wpisu URL StackOverflow, ale ma on znaną długość i dlatego może na stałe zakodować liczbę znaków równości potrzebnych na końcu.

Kirk Liemohn
źródło
@Kirk: Uzupełnij moją odpowiedź za pomocą sprawdzonej matematyki.
AnthonyWJones

Odpowiedzi:

69

Powinno to poprawnie wypełnić: -

 base64 = base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=');
AnthonyWJones
źródło
11
Bah, pokonałeś mnie. Po prostu usunę mój post, bo wygląda prawie tak, jakbym cię skopiował :)
AaronLS
3
Czy to nie doda do trzech znaków '='? Wygląda na to, że będzie ich tylko 0, 1 lub 2.
Kirk Liemohn
1
@Kirk: Jeśli doda 3 znaki, ciąg base64 jest już uszkodzony. Wydaje mi się, że dobrym pomysłem byłoby sprawdzenie poprawności ciągu, powinien on zawierać tylko oczekiwane znaki i długość% 4! = 3.
AnthonyWJones
Hmmm. Próbując tego, nie działa. Wciąż szukam odpowiedzi. Liczba znaków równości po prostu nie wyświetla się prawidłowo.
Kirk Liemohn
2
@AnthonyWJones 'powinien zawierać tylko oczekiwane znaki i długość% 4! = 1 ', prawda?
blueling
173

Sprawdź również klasę HttpServerUtility z metodami UrlTokenEncode i UrlTokenDecode, które obsługują bezpieczne kodowanie i dekodowanie Base64 w formacie URL.

Uwaga 1: wynik nie jest prawidłowym ciągiem Base64. Niektóre niebezpieczne znaki w adresie URL są zastępowane.

Uwaga 2: Wynik różni się od algorytmu base64url w dokumencie 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
źródło
Dzięki za wskazówkę. Spróbuję następnym razem!
Kirk Liemohn
12
Twoja rada była wspaniałym zakończeniem wielogodzinnego poszukiwania odpowiedzi. dzięki
Praesagus
Czy to nie użyje kodowania% dla każdego / + i =? To nie jest tak wydajne jak inna odpowiedź
JoelFan
Nie, zastępuje znaki równości używane do dopełnienia na końcu liczbą i zastępuje plus i ukośnik minusem i podkreśleniem.
Fredrik Haglund,
15
Zwróć uwagę, że UrlTokenEncodenie jest to ściśle base64url , ponieważ zastępuje on dopełnienie „=” wartością „0”, „1” lub „2” w zależności od liczby zastąpionych znaków równości.
ladenedge
28

Za mało punktów do skomentowania, ale na wypadek, gdyby to pomogło, fragment kodu, który Sushil znalazł w podanym łączu (wersja robocza JSON Web Signature ietf) działa podczas kodowania Base 64 jako parametru w adresie URL.

Skopiowany fragment poniżej dla leniwych:

    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
źródło
jest to zgodne z platformą Xamarin w przypadku niekorzystania z System.Web
Reza Mortazavi
Dokładnie to, czego szukałem! Naprawdę dobra opcja dla platformy Xamarin bez konieczności pobierania biblioteki.
Sleeping_Giant
19

Trafiłem tutaj, szukając kodu do kodowania / dekodowania do kodowania base64url, które jest trochę inne niż base64, jak wyjaśniono w pytaniu.

Znaleziono fragment kodu C # w tym dokumencie. Szkic ietf podpisu internetowego JSON

Sushil
źródło
2
To było jedyne rozwiązanie, które zadziałało podczas analizowania wiadomości w GMail API v1 (Message.Raw)
HeyZiko
5

W porównaniu z zaakceptowaną odpowiedzią, oto jak zasadniczo zdekodowałbyś adres URL zakodowany w base64, używając C #:

Rozszyfrować:

string codedValue = "base64encodedUrlHere";

string decoded;
byte[] buffer =  Convert.FromBase64String(codedValue);
decoded = Encoding.UTF8.GetString(buffer);
Chris Halcrow
źródło
może jeśli podasz więcej szczegółów i porównasz z zaakceptowaną odpowiedzią, możesz dostać pozytywną opinię - dzięki
Mauricio Gracia Gutierrez