我基于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中找到关于签名的更多信息(有点冗余)。
为了进一步阐明:
uid
,timestamp
和signature
均取自gigya设置的cookie。为了验证它们没有被欺骗,我采用uid
和timestamp
,并确保可以使用我的 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/