erlang的base64解码问题

    在收到客户端的数字签名signature后,需要对signature做base64的解码。代码如下所示:

 1 validate(SignedRequest) ->
 2     RequestParts = string:split(SignedRequest, "."),%% 数字签名+'.'+algorithm, player_id等信息
 3     Part1Original = lists:nth(1, RequestParts),
 4 
 5     Rem = byte_size(Part1Original) rem 4,
 6     Suffix = case Rem of
 7         2 -> "==";
 8         3 -> "="
 9     end,
10     Part1AppendSuffix = unicode:characters_to_binary([Part1Original, Suffix]), %% 填充'='
11     Part1Replaced1 = unicode:characters_to_binary(string:replace((Part1AppendSuffix), <<"-">>, <<"+">>, all), utf8), %% 替换'-'为'+'
12     Part1Replaced2 = unicode:characters_to_binary(string:replace(Part1Replaced1, <<"_">>, <<"/">>, all), utf8),      %% 替换'_'为'/'
13     Signature = base64:decode(Part1Replaced2),
14 
15     DataHash = crypto:hmac(sha256, <<YOUR_APP_SECERT>>, lists:nth(2, RequestParts)),
16     case Signature =:= DataHash of
17         true ->  success;
18         false -> failure
19     end.

    向signature尾部追加'='的操作是Facebook官方示例中所没有的,Facebook示例在JavaScript的环境中测试时也是正常的,但是在erlang中调用base64:decode()时,若不做此追加处理,总是在最后的环节无法匹配前三个字节。无奈,根据报错提示找来base64.erl的对应代码,如下:

 1 decode([], A) -> A;
 2 decode([$=,$=,C2,C1|Cs], A) ->
 3     Bits2x6 = (b64d(C1) bsl 18) bor (b64d(C2) bsl 12),
 4     Octet1 = Bits2x6 bsr 16,
 5     decode(Cs, [Octet1|A]);
 6 decode([$=,C3,C2,C1|Cs], A) ->
 7     Bits3x6 = (b64d(C1) bsl 18) bor (b64d(C2) bsl 12)
 8     bor (b64d(C3) bsl 6),
 9     Octet1 = Bits3x6 bsr 16,
10     Octet2 = (Bits3x6 bsr 8) band 16#ff,
11     decode(Cs, [Octet1,Octet2|A]);
12 decode([C4,C3,C2,C1| Cs], A) ->
13     Bits4x6 = (b64d(C1) bsl 18) bor (b64d(C2) bsl 12)
14     bor (b64d(C3) bsl 6) bor b64d(C4),
15     Octet1 = Bits4x6 bsr 16,
16     Octet2 = (Bits4x6 bsr 8) band 16#ff,
17     Octet3 = Bits4x6 band 16#ff,
18     decode(Cs, [Octet1,Octet2,Octet3|A]).

可以看到,它只匹配4种情形,而报错时剩余前3个字节无法匹配(注意是逆序处理的),可以推定被解码的字节数不足。这里一篇博客对Base64有很清晰的讲解:

    http://www.ruanyifeng.com/blog/2008/06/base64.html

    在加密前,字节数组是每3个字节为一组被编码为4个字节的。当剩余字节数不足3时,有剩余1个字节和2个字节两种情形,前者被编码为2个字节,需要向尾部追加"==",后者被编码为3个字节,需要向尾部追加"="。而erlang这里报错时,我统计了签名的字节数,是不能被4整除的。因此先判断字节数,并据字节数追加相应的'=',并测试成功。

    使用JavaScript的库crypto-js测试时没有出现此问题,猜想其内部可能根据字节数做了追加处理,具体就不再深究了。

猜你喜欢

转载自www.cnblogs.com/Jackie-Snow/p/9104396.html