Facebook签名请求解码哈希似乎并不完全匹配

时间:2013-09-08 20:59:59

标签: java facebook

我正在使用Facebook并使用Sun Base64解码器在Java servlet中解析签名的请求。虽然签名似乎几乎匹配,但我收到签名失败。我无法弄清楚它为什么不完全匹配,我相信它必须是我的代码的问题。看我正在做什么的最小代码:

BASE64Decoder decoder = new BASE64Decoder();
String[] parts = request.getParameter("signed_request").split("\\.", 2);
String signature = new String(decoder.decodeBuffer(parts[0]), "UTF-8");
String rawData = new String(decoder.decodeBuffer(parts[1]), "UTF-8");

if (!isSignedRequestValid(request, obj, signature, parts[1]))
...

    private boolean isSignedRequestValid(HttpServletRequest request, JSONObject obj, String signature, String data) throws IOException
    {

            String expectedSignature = generateSha256Signature(data, FacebookAppSecretKey);

            if (!signature.equals(expectedSignature))
            {
                log("Facebook signatures do not match, expected: " + expectedSignature + ", received: " + signature);
                return false;
            }
         }


    private String generateSha256Signature(String data, String key) throws Exception
    {
      java.net.URLDecoder decoder = new java.net.URLDecoder();
      data = decoder.decode(data, "UTF-8");  // mostly here for testing, doesn't seem to make a difference
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(secretKey);
        byte[] hmacData = mac.doFinal(data.getBytes("UTF-8"));
        return new String(hmacData);
    }

我遇到两个问题......首先,预期的vs提供的签名是32对33字节。但是,如果我只比较前32个字节,有时它们是相等的,但是当它不匹配时,它恰好不匹配一个比特。例如,打印出索引和值的一个例子是:

index 14 : [15] , [15]
index 15 : [71] , [71]
index 16 : [-10] , [-26]    <-- note the different here, which is 16, or one bit
index 17 : [28] , [28]
index 18 : [60] , [60]

我是解析Facebook请求的新手,所以我不得不相信我在代码中遗漏了一些东西,但是我没有看到它,并且已经玩了很长时间以至于需要帮助。我在网上其他任何地方都没有找到答案。 谢谢!

2 个答案:

答案 0 :(得分:2)

似乎有两个问题......首先,解码提供的Facebook签名解码为33个字节而不是32个。所以我只使用前32个字节(不关心最后一个是什么..我只是填充功能。

另一个问题是+到 - 的URL编码。在base 64解码之前使用URLDecoder解码Facebook签名并没有解决问题。相反,我这样做了:

    BASE64Decoder decoder = new BASE64Decoder();
    String[] parts = request.getParameter("signed_request").split("\\.", 2);
    String rawSig = parts[0];
    rawSig = rawSig.replaceAll("\\-", "\\+");
    byte[] signature = decoder.decodeBuffer(rawSig);

这足以使前32位始终与计算的签名匹配。我觉得奇怪的是URL编码是以URLDecoder.decode()没有修复它的方式完成的。如果有人对这个原因有任何见解,肯定会很好奇。

答案 1 :(得分:0)

显然,您还必须将_变为/

发现了一件事on Wikipedia

  

存在针对URL变体的已修改Base64,其中标准Base64的“+”和“/”字符分别替换为“ - ”和“_”,