Как декодировать base64 url ​​в python? - PullRequest
20 голосов
/ 21 июля 2010

Для приложений Facebook fbml Facebook отправляет в параметре подписанный запрос, объясненный здесь:

http://developers.facebook.com/docs/authentication/canvas

Они дали версию php декодирования этого подписанного запроса:

http://pastie.org/1054154

Как сделать то же самое в python?

Я пробовал модуль base64, но получаю неправильную ошибку заполнения:

>>> base64.urlsafe_b64decode("eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEyNzk3NDYwMDAsIm9hdXRoX3Rva2VuIjoiMjk1NjY2Njk1MDY0fDIuRXpwem5IRVhZWkJVZmhGQ2l4ZzYzUV9fLjM2MDAuMTI3OTc0NjAwMC0xMDAwMDA0ODMyNzI5MjN8LXJ6U1pnRVBJTktaYnJnX1VNUUNhRzlNdEY4LiIsInVzZXJfaWQiOiIxMDAwMDA0ODMyNzI5MjMifQ")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/base64.py", line 112, in urlsafe_b64decode
    return b64decode(s, '-_')
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/base64.py", line 76, in b64decode
    raise TypeError(msg)
TypeError: Incorrect padding

Ответы [ 7 ]

25 голосов
/ 28 июля 2010

Я поделился фрагментом кода для синтаксического анализа параметра Sign_request в приложении на основе Python для Facebook: http://sunilarora.org/parsing-signedrequest-parameter-in-python-bas:

import base64
import hashlib
import hmac
import simplejson as json

def base64_url_decode(inp):
    padding_factor = (4 - len(inp) % 4) % 4
    inp += "="*padding_factor 
    return base64.b64decode(unicode(inp).translate(dict(zip(map(ord, u'-_'), u'+/'))))

def parse_signed_request(signed_request, secret):

    l = signed_request.split('.', 2)
    encoded_sig = l[0]
    payload = l[1]

    sig = base64_url_decode(encoded_sig)
    data = json.loads(base64_url_decode(payload))

    if data.get('algorithm').upper() != 'HMAC-SHA256':
        log.error('Unknown algorithm')
        return None
    else:
        expected_sig = hmac.new(secret, msg=payload, digestmod=hashlib.sha256).digest()

    if sig != expected_sig:
        return None
    else:
        log.debug('valid signed request received..')
return data
23 голосов
/ 31 марта 2012

1001 * попробовать *

s = 'iEPX-SQWIR3p67lj_0zigSWTKHg'
base64.urlsafe_b64decode(s + '=' * (4 - len(s) % 4))

как написано здесь

19 голосов
/ 22 июля 2010

Видимо, вы пропустили последние два символа при копировании исходной строки в кодировке base64. Добавьте к входной строке два знака равенства (=), и она будет правильно декодирована.

5 голосов
/ 02 апреля 2015

Альтернатива решению @ dae.eklen, вы можете добавить === к нему:

s = 'iEPX-SQWIR3p67lj_0zigSWTKHg'
base64.urlsafe_b64decode(s + '===')

Это работает, потому что Python жалуется только на отсутствие заполнения, но не на дополнительное заполнение.

1 голос
/ 15 января 2018

Удивительно, но в настоящее время принятый ответ не совсем верен. Как и в некоторых других ответах, это называется кодировкой base64url и является частью RFC7515 .

По сути, они заменили символы '+' и '/' на '-' и '_' соответственно; и дополнительно удалил все завершающие символы '=', потому что вы всегда можете определить количество пропущенных символов, просто взглянув на длину закодированной строки.

Вот иллюстративный пример из RFC7515 в C #:

 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
 }
1 голос
/ 24 января 2012
import base64
import simplejson as json

def parse_signed_request( signed_request ):
    encoded_sig, payload = signed_request.split('.',2)
    data = json.loads(base64.b64decode( payload.replace('-_', '+/') ))
    return data
0 голосов
/ 02 мая 2019

Это правильное решение. В python есть base64.b64encode, но он кодирует только base64, и он отличается от кодировки url base64. Вот правильный набор шагов для преобразования формы base64encoded в base64urlencoded string:
1. Из полученной строки замените «/» на «_» и «+» на «-»
2. Снимите конечный "==".

Et вуаля! Это сделает его допустимой строкой для декодирования url base64. Кстати, эта ссылка в ответе @ dae.eklen выше не работает.

...