2

我正在使用 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);
    }

我遇到了两个问题......首先,预期与提供的签名是 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 请求的新手,所以我必须相信我在代码中遗漏了一些东西,但我没有看到它,并且已经玩了足够长的时间来需要帮助。而且我在网上其他任何地方都没有看到答案。谢谢!

4

2 回答 2

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() 没有修复它的方式完成的。如果有人对此原因有任何见解,肯定会很好奇。

于 2013-09-10T23:39:33.423 回答
0

显然你也必须子_成为/

在维基百科上发现了一个东西:

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

于 2015-01-26T04:40:16.910 回答