我基于Gigya的instructions for constructing a signature编写了一种方法,用于根据指定的时间戳和UID验证gigya签名。这是Gigya的代码,用于执行此操作:

string constructSignature(string timestamp, string UID, string secretKey) {
    // Construct a "base string" for signing
    baseString = timestamp + "_" + UID;
    // Convert the base string into a binary array
    binaryBaseString = ConvertUTF8ToBytes(baseString);
    // Convert secretKey from BASE64 to a binary array
    binaryKey = ConvertFromBase64ToBytes(secretKey);
    // Use the HMAC-SHA1 algorithm to calculate the signature
    binarySignature = hmacsha1(binaryKey, baseString);
    // Convert the signature to a BASE64
    signature = ConvertToBase64(binarySignature);
    return signature;
}

[sic]

这是我的方法(省略了异常处理):
public boolean verifyGigyaSig(String uid, String timestamp, String signature) {

    // Construct the "base string"
    String baseString = timestamp + "_" + uid;

    // Convert the base string into a binary array
    byte[] baseBytes = baseString.getBytes("UTF-8");

    // Convert secretKey from BASE64 to a binary array
    String secretKey = MyConfig.getGigyaSecretKey();
    byte[] secretKeyBytes = Base64.decodeBase64(secretKey);

    // Use the HMAC-SHA1 algorithm to calculate the signature
    Mac mac = Mac.getInstance("HmacSHA1");
    mac.init(new SecretKeySpec(secretKeyBytes, "HmacSHA1"));
    byte[] signatureBytes = mac.doFinal(baseBytes);

    // Convert the signature to a BASE64
    String calculatedSignature = Base64.encodeBase64String(signatureBytes);

    // Return true iff constructed signature equals specified signature
    return signature.equals(calculatedSignature);
}

即使不需要,此方法也会返回false。有人可以发现我的实现有问题吗?我想知道调用方或gigya本身是否有问题-“您的方法 checkout ”是有效的答案

我正在使用Apache Commons的 Base64 类进行编码。

如果有帮助的话,还可以在Gigya's FAQ中找到关于签名的更多信息(有点冗余)。

为了进一步阐明:uidtimestampsignature均取自gigya设置的cookie。为了验证它们没有被欺骗,我采用uidtimestamp,并确保可以使用我的 secret key 重构signature。当我不应该在我的方法,前端或gigya本身的过程中某个时候出现故障或格式问题时,它就会失败。这个问题的目的实质上是为了排除上述方法中的错误。

注意:我也尝试了URL编码uid:
String baseString = timestamp + "_" + URLEncoder.encode(uid, "UTF-8");

尽管我认为这并不重要,因为它只是一个整数。 timestamp也是如此。

更新:

根本问题已解决,但问题本身仍然悬而未决。有关更多详细信息,请参见my answer

更新2:

事实证明,我对我使用的是哪个apache的Base64类感到困惑-我的代码不是使用Commons Codec version而是Commons Net version。造成这种困惑的原因是我的项目中有大量的第三方库,而我多年来对Apache库中的许多Base64实现却一无所知-我现在意识到Commons Codec可以解决这种情况。在编码方面,我似乎迟到了。

切换到Commons Codec的版本后,该方法将正常运行。

由于@erickson被发现,因此我将奖励his answer,但请对这两个答案都予以赞扬,以提供出色的见解!我将暂时保留悬赏金,以便他们得到应有的关注。

最佳答案

我将仔细研究您的Base-64编码和解码。

您正在为此使用第三方库吗?如果是这样,哪一个?如果没有,您是否可以发布自己的实现或至少一些示例输入和输出(用十六进制表示字节)?

有时,使用的“额外” Base-64字符会有所不同(用“/”和“+”代替字符)。也可以省略填充,这将导致字符串比较失败。

正如我所怀疑的,是造成这种差异的原因是Base-64编码。但是,导致问题的原因是尾随空白,而不是填充或符号上的差异。

您使用的 encodeBase64String() 方法始终将CRLF附加到其输出中。 Gigya签名不包含此尾随空格。比较这些字符串是否相等只会失败,原因是空格之间存在这种差异。

使用Commons Codec库(而不是Commons Net)中的 encodeBase64String() 创建有效的签名。

如果我们将签名计算排​​除在外,并针对Gigya SDK的验证程序测试其结果,则可以看到删除CRLF会创建有效的签名:

public static void main(String... argv)
  throws Exception
{
  final String u = "";
  final String t = "";
  final String s = MyConfig.getGigyaSecretKey();

  final String signature = sign(u, t, s);
  System.out.print("Original valid? ");
  /* This prints "false" */
  System.out.println(SigUtils.validateUserSignature(u, t, s, signature));

  final String stripped = signature.replaceAll("\r\n$", "");
  System.out.print("Stripped valid? ");
  /* This prints "true" */
  System.out.println(SigUtils.validateUserSignature(u, t, s, stripped));
}

/* This is the original computation included in the question. */
static String sign(String uid, String timestamp, String key)
  throws Exception
{
  String baseString = timestamp + "_" + uid;
  byte[] baseBytes = baseString.getBytes("UTF-8");
  byte[] secretKeyBytes = Base64.decodeBase64(key);
  Mac mac = Mac.getInstance("HmacSHA1");
  mac.init(new SecretKeySpec(secretKeyBytes, "HmacSHA1"));
  byte[] signatureBytes = mac.doFinal(baseBytes);
  return Base64.encodeBase64String(signatureBytes);
}

关于java - 构造和验证Gigya签名,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10033963/

10-10 09:38