我正在使用PHP中的SSO实现,该实现对使用C#编写的系统进行身份验证。这里有一些伪代码来演示:
$token = "MqsXexqpYRUNAHR_lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l_tOirhThQYIbfWYErgu4bDwl-7brVhXTWnJNQ2";
$id = "bob@company.com";
$ssokey = "7MpszrQpO95p7H";
$idAndKey = $id . $ssokey;
$salt = base64_decode(substr($token, 0, -1));
$hashed = hash_pbkdf2("sha256", $idAndKey, mb_convert_encoding($salt, 'UTF-16LE'), 1000, 24, false);
$data = base64_encode($hashed);
输出:NWZiMTBhZmNhNTlmYzMxMTEzMThhZmVl
这是我集成的系统中的C#版本:
var token = "MqsXexqpYRUNAHR_lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l_tOirhThQYIbfWYErgu4bDwl-7brVhXTWnJNQ2";
var id = "bob@company.com";
var ssokey = "7MpszrQpO95p7H";
string idAndKey = id + ssokey;
var salt = HttpServerUtility.UrlTokenDecode(token);
var pbkdf2 = new Rfc2898DeriveBytes(idAndKey, salt) {IterationCount = 1000};
var key = HttpServerUtility.UrlTokenEncode(pbkdf2.GetBytes(24));
Console.WriteLine(key.ToString());
输出:aE1k9-djZ66WbUATqdHbWyJzskMI5ABS0
我无法弄清楚如何让我的PHP代码做同样的事情。我感觉它在salt
代。
我试图将C#HttpServerUtility.UrlTokenDecode
函数转换为PHP,如下所示:
function UrlTokenDecode($token) {
$numPadChars = substr($token, -1);
// add the padded count to the end
$salt = substr($token, 0, -1) . $numPadChars;
// Transform the "-" to "+", and "*" to "/"
$salt = str_replace('-', '+', str_replace('*', '/', $salt));
// base64_decode
$salt = base64_decode($salt);
return $salt;
}
那并没有让我到达我需要去的地方。 HALP!
这适用于Absorb LMS。他们的方法文档在这里:https://support.absorblms.com/hc/en-us/articles/222446647-Incoming-Absorb-Single-Sign-On#Methods
谢谢!
答案 0 :(得分:3)
我根本不知道php,但我仍然可以帮助。首先,正如我的评论中所述,C#中的buildToolsVersion "26.0.1"
使用SHA1作为哈希函数,而不是SHA256,与文档所说的无关。
接下来,Rfc2898DeriveBytes
(和UrlTokenDecode
)是我在实践中很少看到的奇怪的事情。它将常规base64转换为“url safe”版本,如下所示:
因此要复制,您需要Encode
,替换,删除填充,然后添加填充长度作为字符。因此,如果您的base64字符串以base64_encode
结尾 - 您将其删除并在末尾添加“2”。如果没有填充 - 你添加“0”。
因此,为了解码该字符串,您需要进行替换,然后删除最后一个字符并将该字符添加到该字符所指示的末尾。
所以字符串
==
正常base64是 MqsXexqpYRUNAHR / lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l / tOirhThQYIbfWYErgu4bDwl + 7brVhXTWnJNQ ==
然后,我不知道你为什么这样做
MqsXexqpYRUNAHR_lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l_tOirhThQYIbfWYErgu4bDwl-7brVhXTWnJNQ2
只需删除它(虽然因为我不知道php - 你可能有某种原因这样做,但我无法想象哪个,所以要小心)。
然后作为其他答案状态 - hash_pbkdf2()的最后一个参数应为true。
进行此更改后,您的代码将起作用(我使用的令牌已转换为普通的base64字符串):
mb_convert_encoding($salt, 'UTF-16LE')
产生预期答案(在正常的base64中 - 你需要“url encode”它以获得完全匹配)。
答案 1 :(得分:2)
我已经烧掉了比此更多的时间,但是虽然这不是一个完整的答案,但我找到的几个主要问题是:
HttpServerUtility.UrlToken(De|En)code()
使用需要复制的base64的url-safe变体。
function base64url_encode($bin) {
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($bin));
}
function base64url_decode($str) {
return base64_decode(str_replace(['-', '_'], ['+', '/'], $str));
}
当您解码令牌时,结果是二进制字符串,并尝试通过mb_convert_encoding运行它以更改字节序[我发现可怕的博客文章]将不会按您的想法执行。您可以尝试以下操作,但令牌具有奇数个字节,无论您以何种方式查看它都是有问题的。 [编辑:最后只有一个裸\x0d
回车符?]
function swapEndian16($in) {
$out = '';
foreach(str_split($in, 2) as $chunk) {
$out .= $chunk[1] . $chunk[0];
}
return $out;
}
hash_pbkdf2()
的最后一个参数应为true
,否则您将获得十六进制编码的哈希而不是原始字节。
我真正建议的是询问您的供应商是否对实现此目标有任何见解。有可能是某人已经拥有并通过他们的集成解决了这个问题。
编辑:使用来自@Evk的答案中的新信息,这里有一些简单命名的函数,用于与C#的辉煌 base64 URL编码兼容:
function dumb_base64url_encode($bin) {
return preg_replace_callback(
'/(=*)$/',
function($matches){
return strlen($matches[0]);
},
str_replace(
['+', '/'],
['-', '_'],
base64_encode($bin)
),
1
);
}
function dumb_base64url_decode($str) {
return base64_decode(
str_replace(
['-', '_'],
['+', '/'],
substr($str, 0, -1)
)
);
}
现在,使用未经“更正”的令牌:
$token = "MqsXexqpYRUNAHR_lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l_tOirhThQYIbfWYErgu4bDwl-7brVhXTWnJNQ2";
$id = "bob@company.com";
$ssokey = "7MpszrQpO95p7H";
$idAndKey = $id . $ssokey;
$salt = dumb_base64url_decode($token);
$hashed = hash_pbkdf2("sha1", $idAndKey, $salt, 1000, 24, true);
$data = dumb_base64url_encode($hashed);
echo $data; // output: aE1k9-djZ66WbUATqdHbWyJzskMI5ABS0
并且不要冒出那些标记正确的答案,我认为@Evk有最重要的部分排序。