C# SSO 哈希迁移到 PHP
C# SSO hashing migrated to PHP
我正在研究 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;
}
这并没有让我到达我需要去的地方。哈尔普!
这是用于 Absorb LMS。他们的方法文档在这里:https://support.absorblms.com/hc/en-us/articles/222446647-Incoming-Absorb-Single-Sign-On#Methods
谢谢!
我已经花费了比我应该花的更多的时间,但虽然这不是一个完整的答案,但我发现的几个主要问题是:
- 散列算法是 SHA1,不是 SHA256。 [正如@Evk 已经指出的那样]
HttpServerUtility.UrlToken(De|En)code()
使用需要复制的 url-safe base64 变体。
function base64url_encode($bin) {
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($bin));
}
function base64url_decode($str) {
return base64_decode(str_replace(['-', '_'], ['+', '/'], $str));
}
当你解码令牌时,结果是一个二进制字符串,并试图 运行 通过 mb_convert_encoding 来改变 endian-ness [我发现那很糟糕博客 post 也不会按照您的想法去做。您可以尝试以下操作,但令牌的字节数是奇数,无论您以何种方式查看它都是有问题的。 [编辑:最后是否只有一个光秃秃的 \x0d
马车 return?]
function swapEndian16($in) {
$out = '';
foreach(str_split($in, 2) as $chunk) {
$out .= $chunk[1] . $chunk[0];
}
return $out;
}
hash_pbkdf2()
的最后一个参数应该是 true
,否则你会得到一个 hex-encoded 散列而不是原始字节。
我真正的建议是询问您的供应商是否对实现此目标有任何见解。很可能有人已经通过他们的集成解决了这个问题。
编辑: 根据@Evk 的回答中的新信息,这里有一些 sassily-named 与 C# brilliant 兼容的函数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)
)
);
}
所以现在,使用 un-"corrected" 令牌:
$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 已经对最重要的部分进行了排序。
我完全不知道php,但我认为还是可以提供帮助。首先,如我的评论所述,C# 中的 Rfc2898DeriveBytes
使用 SHA1 作为哈希函数,而不是 SHA256,与您的文档所说的无关。
接下来,UrlTokenDecode
(和Encode
)是我在实践中很少见到的比较奇怪的东西。它将常规 base64 转换为 "url safe" 版本,如下所示:
- 将“+”替换为“-”
- 将“/”替换为“_”
- 删除填充(末尾的'==')并附加删除填充的长度作为数字作为最后一个字符(如果没有填充 - 它仍然附加“0 ”)。这一步对我来说没有任何意义,但它就是这样工作的。
因此,要复制您需要 base64_encode
、替换、删除填充,然后添加填充长度作为字符。因此,如果您的 base64 字符串以 ==
结尾 - 您将其删除并在末尾添加“2”。如果没有填充 - 您添加“0”。
所以要解码该字符串,您需要进行反向替换,然后删除最后一个字符并在该字符指示的末尾添加那么多“=”。
所以字符串
MqsXexqpYRUNAHR_lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l_tOirhThQYIbfWYErgu4bDwl-7brVhXTWnJNQ2
正常的base64是
MqsXexqpYRUNAHR/lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l/tOirhThQYIbfWYErgu4bDwl+7brVhXTWnJNQ==
那么,我不知道你为什么要那样做
mb_convert_encoding($salt, 'UTF-16LE')
只需删除它(尽管我不知道 php - 您这样做可能有某些原因,但我无法想象是哪一个,所以要小心)。
然后如其他答案所述 - hash_pbkdf2() 的最后一个参数应该为真。
进行此更改后,您的代码将起作用(我使用已转换为普通 base64 字符串的令牌):
$token = "MqsXexqpYRUNAHR/lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l/tOirhThQYIbfWYErgu4bDwl+7brVhXTWnJNQ==";
$id = "bob@company.com";
$ssokey = "7MpszrQpO95p7H";
$idAndKey = $id . $ssokey;
$salt = base64_decode($token);
$hashed = hash_pbkdf2("sha1", $idAndKey, $salt, 1000, 24, true);
$data = base64_encode($hashed);
echo $data;
产生预期的答案(在正常的 base64 中 - 你需要 "url encode" 它以获得精确匹配)。
我正在研究 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;
}
这并没有让我到达我需要去的地方。哈尔普!
这是用于 Absorb LMS。他们的方法文档在这里:https://support.absorblms.com/hc/en-us/articles/222446647-Incoming-Absorb-Single-Sign-On#Methods
谢谢!
我已经花费了比我应该花的更多的时间,但虽然这不是一个完整的答案,但我发现的几个主要问题是:
- 散列算法是 SHA1,不是 SHA256。 [正如@Evk 已经指出的那样]
HttpServerUtility.UrlToken(De|En)code()
使用需要复制的 url-safe base64 变体。function base64url_encode($bin) { return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($bin)); } function base64url_decode($str) { return base64_decode(str_replace(['-', '_'], ['+', '/'], $str)); }
当你解码令牌时,结果是一个二进制字符串,并试图 运行 通过 mb_convert_encoding 来改变 endian-ness [我发现那很糟糕博客 post 也不会按照您的想法去做。您可以尝试以下操作,但令牌的字节数是奇数,无论您以何种方式查看它都是有问题的。 [编辑:最后是否只有一个光秃秃的
\x0d
马车 return?]function swapEndian16($in) { $out = ''; foreach(str_split($in, 2) as $chunk) { $out .= $chunk[1] . $chunk[0]; } return $out; }
hash_pbkdf2()
的最后一个参数应该是true
,否则你会得到一个 hex-encoded 散列而不是原始字节。
我真正的建议是询问您的供应商是否对实现此目标有任何见解。很可能有人已经通过他们的集成解决了这个问题。
编辑: 根据@Evk 的回答中的新信息,这里有一些 sassily-named 与 C# brilliant 兼容的函数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)
)
);
}
所以现在,使用 un-"corrected" 令牌:
$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 已经对最重要的部分进行了排序。
我完全不知道php,但我认为还是可以提供帮助。首先,如我的评论所述,C# 中的 Rfc2898DeriveBytes
使用 SHA1 作为哈希函数,而不是 SHA256,与您的文档所说的无关。
接下来,UrlTokenDecode
(和Encode
)是我在实践中很少见到的比较奇怪的东西。它将常规 base64 转换为 "url safe" 版本,如下所示:
- 将“+”替换为“-”
- 将“/”替换为“_”
- 删除填充(末尾的'==')并附加删除填充的长度作为数字作为最后一个字符(如果没有填充 - 它仍然附加“0 ”)。这一步对我来说没有任何意义,但它就是这样工作的。
因此,要复制您需要 base64_encode
、替换、删除填充,然后添加填充长度作为字符。因此,如果您的 base64 字符串以 ==
结尾 - 您将其删除并在末尾添加“2”。如果没有填充 - 您添加“0”。
所以要解码该字符串,您需要进行反向替换,然后删除最后一个字符并在该字符指示的末尾添加那么多“=”。
所以字符串
MqsXexqpYRUNAHR_lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l_tOirhThQYIbfWYErgu4bDwl-7brVhXTWnJNQ2
正常的base64是 MqsXexqpYRUNAHR/lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l/tOirhThQYIbfWYErgu4bDwl+7brVhXTWnJNQ==
那么,我不知道你为什么要那样做
mb_convert_encoding($salt, 'UTF-16LE')
只需删除它(尽管我不知道 php - 您这样做可能有某些原因,但我无法想象是哪一个,所以要小心)。
然后如其他答案所述 - hash_pbkdf2() 的最后一个参数应该为真。
进行此更改后,您的代码将起作用(我使用已转换为普通 base64 字符串的令牌):
$token = "MqsXexqpYRUNAHR/lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l/tOirhThQYIbfWYErgu4bDwl+7brVhXTWnJNQ==";
$id = "bob@company.com";
$ssokey = "7MpszrQpO95p7H";
$idAndKey = $id . $ssokey;
$salt = base64_decode($token);
$hashed = hash_pbkdf2("sha1", $idAndKey, $salt, 1000, 24, true);
$data = base64_encode($hashed);
echo $data;
产生预期的答案(在正常的 base64 中 - 你需要 "url encode" 它以获得精确匹配)。