How to decode base64 url in python?
For Facebook fbml Apps Facebook is sending in a signed_request parameter explained here:
http://developers.facebook.com/docs/authentication/canvas
They have given the php version of decoding this signed request:
http://pastie.org/1054154
How to do the same in python?
I tried base64 module but I am getting Incorrect padding error:
>>> 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 Type开发者_开发问答Error(msg)
TypeError: Incorrect padding
try
s = 'iEPX-SQWIR3p67lj_0zigSWTKHg'
base64.urlsafe_b64decode(s + '=' * (4 - len(s) % 4))
as it is written here
I have shared a code snippet for parsing signed_request parameter in a python based facebook canvas application at 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
Apparently you missed the last two characters when copying the original base64-encoded string. Suffix the input string with two is-equal (=) signs and it will be decoded correctly.
Alternative to @dae.eklen's solution, you can append ===
to it:
s = 'iEPX-SQWIR3p67lj_0zigSWTKHg'
base64.urlsafe_b64decode(s + '===')
This works because Python only complains about missing padding, but not extra padding.
Surprising, but currently accepted answer is not exactly correct. Like some other answers stated, it's something called base64url encoding, and it's a part of RFC7515.
Basically, they replaced '+' and '/' chars by '-' and '_' respectively; and additionally removed any trailing '=' chars, because you can always tell how many chars you're missing, just by looking at the encoded string length.
Here's illustrative example from RFC7515 in 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
}
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
This is the right solution. In python there is base64.b64encode but that only base64 encodes and its is different from base64 url encoding. Here is the right set to of steps to convert form base64encoded to base64urlencoded string:
1. From the resultant string, replace "/" with "_" and "+" with "-"
2. Strip the trailing "==".
Et voila! That will make it a valid string for base64 url decoding. Btw, that link in @dae.eklen 's answer above is broken now.
If you are sending your base64
string from .net
as a param it seems that chars that have special meaning in the URI ie +
or /
are replaced with " "
spaces.
So before you send your string in .net you should probably do something like this
base64img.Replace("+", "-").Replace("/", "_"))
Then in python decode the string (also add '=' until the length is divisible by 4)
def decode_base64(data):
data += '=' * (len(data) % 4)
return base64.urlsafe_b64decode(data)
Further if you want to use the image in openCV
def get_cv2_img_from_base64(base_64_string):
data = decode_base64(base_64_string)
np_data = np.frombuffer(data, dtype=np.uint8)
return cv2.imdecode(np_data, cv2.IMREAD_UNCHANGED)
My solution was to translate old c# code to python.
import base64
def base64_encode_url(value):
encoded = str(base64.b64encode(bytes(value, "utf-8")), 'utf-8')
return encoded.replace('=', '').replace('+', '-').replace('/', '_')
def base64_decode_url(data):
value = data.replace('-', '+').replace('_', '/')
value += '=' * (len(value) % 4)
return str(base64.urlsafe_b64decode(value), 'utf-8')
just
base64.urlsafe_b64decode(s)
精彩评论