C# C SSO哈希迁移到PHP

C# C SSO哈希迁移到PHP,c#,php,pbkdf2,C#,Php,Pbkdf2,我正在用PHP开发一个SSO实现,该实现对用C编写的系统进行身份验证。下面是一些伪代码来演示: $token = "MqsXexqpYRUNAHR_lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l_tOirhThQYIbfWYErgu4bDwl-7brVhXTWnJNQ2"; $id = "bob@company.com"; $ssokey = "7MpszrQpO95p7H"; $idAndKey = $id . $ssokey; $salt = base64_decode(

我正在用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);
此输出:NWZIMTBHZMNTNTLMYZMXMTEZMTHZMVL

这是我正在集成的系统的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代码做同样的事情。我有一种感觉,这是在盐的一代

我曾尝试将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;
}
这并没有让我达到我需要去的地方。停

这是用于吸收LMS的。其方法的文件如下:


谢谢

我在这方面已经花了很多时间,但虽然这不是一个完整的答案,但我发现的几个主要问题是:

散列算法是SHA1,而不是SHA256。[正如@Evk已经指出的那样] HttpServerUtility.UrlTokenDe | Encode使用需要复制的url安全的base64变体

function base64url_encode($bin) {
    return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($bin));
}

function base64url_decode($str) {
    return base64_decode(str_replace(['-', '_'], ['+', '/'], $str));
}
当你解码标记时,结果是一个二进制字符串,试图通过mb_convert_编码来改变endian的长度[我发现那篇糟糕的博客文章也]不会像你想的那样。您可以尝试以下方法,但该令牌的字节数为奇数,无论您从哪个角度看,这都是有问题的。[编辑:末尾是否只有一个空的\x0d回车?]

function swapEndian16($in) {
    $out = '';
    foreach(str_split($in, 2) as $chunk) {
        $out .= $chunk[1] . $chunk[0];
    }
    return $out;
}
最后一个参数应该为true,否则您将得到一个十六进制编码的哈希值,而不是原始字节

实际上,我的建议是询问您的供应商是否对实现这一点有任何见解。很可能有人已经通过集成解决了这个问题

编辑:根据@Evk答案中的新信息,以下是一些与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)
        )
    );
}
现在,使用未更正的令牌:

$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已经把最重要的部分整理好了。

我已经花了很多时间在这个问题上,但虽然这不是一个完整的答案,但我发现的几个主要问题是:

散列算法是SHA1,而不是SHA256。[正如@Evk已经指出的那样] HttpServerUtility.UrlTokenDe | Encode使用需要复制的url安全的base64变体

function base64url_encode($bin) {
    return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($bin));
}

function base64url_decode($str) {
    return base64_decode(str_replace(['-', '_'], ['+', '/'], $str));
}
当你解码标记时,结果是一个二进制字符串,试图通过mb_convert_编码来改变endian的长度[我发现那篇糟糕的博客文章也]不会像你想的那样。您可以尝试以下方法,但该令牌的字节数为奇数,无论您从哪个角度看,这都是有问题的。[编辑:末尾是否只有一个空的\x0d回车?]

function swapEndian16($in) {
    $out = '';
    foreach(str_split($in, 2) as $chunk) {
        $out .= $chunk[1] . $chunk[0];
    }
    return $out;
}
最后一个参数应该为true,否则您将得到一个十六进制编码的哈希值,而不是原始字节

实际上,我的建议是询问您的供应商是否对实现这一点有任何见解。很可能有人已经通过集成解决了这个问题

编辑:根据@Evk答案中的新信息,以下是一些与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)
        )
    );
}
现在,使用未更正的令牌:

$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安全版本,如下所示:

将“+”替换为“-” 将“/”替换为“\u1” 删除结尾处的填充“==”,如果没有填充,则将删除的填充长度作为数字作为最后一个字符追加—它仍然追加0。这一步对我来说毫无意义,但它是如何工作的。 所以要进行复制,需要对64_进行base64编码、替换、删除填充,然后添加填充长度作为字符。因此,如果base64字符串以==-结尾,则删除该字符串并在末尾添加2。如果没有填充,则添加0

因此,要解码该字符串,您需要进行替换,然后删除最后一个字符,并将该字符所指示的“=”添加到末尾

所以串

MqsXexqpYRUNAHR_lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l_tOirhThQYIbfWYErgu4bDwl-7brVhXTWnJNQ2
在正常情况下,base64是 MqsXexqpYRUNAHR/LHKPric1G1BYH6BfNvPageKual8MF80L/ToirhqYibfWyergu4BDWL+7brVhXTWnJNQ==

那么,我不知道你为什么这么做

mb_convert_encoding($salt, 'UTF-16LE')
尽管我不懂php,但还是删除它吧——你这么做可能是有原因的,但我无法想象,所以要小心

然后,正如其他答案所述,hash_pbkdf2的最后一个参数应该为true

进行此更改后,您的代码将正常工作。我使用的令牌已转换为普通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编码以获得精确匹配。

我一点也不懂php,但我认为仍然可以提供帮助。冷杉 st,正如我在评论中所说的,C中的Rfc2898DeriveBytes使用SHA1作为哈希函数,而不是SHA256,不管您的文档怎么说

其次,UrlTokenDecode和Encode是我在实践中很少看到的非常奇怪的事情。它将常规base64转换为url安全版本,如下所示:

将“+”替换为“-” 将“/”替换为“\u1” 删除结尾处的填充“==”,如果没有填充,则将删除的填充长度作为数字作为最后一个字符追加—它仍然追加0。这一步对我来说毫无意义,但它是如何工作的。 所以要进行复制,需要对64_进行base64编码、替换、删除填充,然后添加填充长度作为字符。因此,如果base64字符串以==-结尾,则删除该字符串并在末尾添加2。如果没有填充,则添加0

因此,要解码该字符串,您需要进行替换,然后删除最后一个字符,并将该字符所指示的“=”添加到末尾

所以串

MqsXexqpYRUNAHR_lHkPRic1g1BYhH6bFNVPagEkuaL8Mf80l_tOirhThQYIbfWYErgu4bDwl-7brVhXTWnJNQ2
在正常情况下,base64是 MqsXexqpYRUNAHR/LHKPric1G1BYH6BfNvPageKual8MF80L/ToirhqYibfWyergu4BDWL+7brVhXTWnJNQ==

那么,我不知道你为什么这么做

mb_convert_encoding($salt, 'UTF-16LE')
尽管我不懂php,但还是删除它吧——你这么做可能是有原因的,但我无法想象,所以要小心

然后,正如其他答案所述,hash_pbkdf2的最后一个参数应该为true

进行此更改后,您的代码将正常工作。我使用的令牌已转换为普通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编码以获得精确匹配。

一件事是Rfc2898DeriveBytes使用sha1作为哈希函数,而不是sha256。谢谢!医生特别说明了sha256,所以我很生气。我已经更新了代码,但仍然没有得到想要的结果。还有其他想法吗?一件事是Rfc2898DeriveBytes使用sha1作为散列函数,而不是sha256。谢谢!医生特别说明了sha256,所以我很生气。我已经更新了代码,但仍然没有得到想要的结果。还有其他想法吗?这太棒了。在你的答案和@Sammitch之间,我希望我能将两者都标记为正确,我现在得到了正确的输出。sha1是这个谜题的关键,但是正确的编码/解码认为我已经有所不同了。啊,在base64末尾添加填充的东西真是太蠢了。至少在PHP中,您可以在解码之前删除填充字符,而不会产生不良影响。我在我的答案中添加了一些应该有用的时髦函数。@Sammitch是的,直到这个问题我才知道有这样的方法存在。很好,几乎没有人真的使用这种奇怪的safe base64。URL-safe base64实际上开始相当频繁地出现,主要是在JWT实现中。奇数部分是在一个数字上加上填充,而不仅仅是将其丢弃。如果base64解码函数需要填充,则可以轻松地将字符串填充为4的倍数。¯\_ツ_/当然,url安全的base64是可以的,但这个特定的实现不是。这太棒了。在你的答案和@Sammitch之间,我希望我能将两者都标记为正确,我现在得到了正确的输出。sha1是这个谜题的关键,但是正确的编码/解码认为我已经有所不同了。啊,在base64末尾添加填充的东西真是太蠢了。至少在PHP中,您可以在解码之前删除填充字符,而不会产生不良影响。我在我的答案中添加了一些应该有用的时髦函数。@Sammitch是的,直到这个问题我才知道有这样的方法存在。很好,几乎没有人真的使用这种奇怪的safe base64。URL-safe base64实际上开始相当频繁地出现,主要是在JWT实现中。奇数部分是在一个数字上加上填充,而不仅仅是将其丢弃。如果base64解码函数需要填充,则可以轻松地将字符串填充为4的倍数。¯\_ツ_/当然,url安全的base64是可以的,但是这个特定的实现不是。谢谢@Sammitch。这看起来很棒。我意识到我们没有整理出未更正的代币。虽然这不是一项非常复杂的任务,但我很欣赏你的回答的彻底性谢谢@Sammitch。这看起来很棒。我意识到我们没有整理出未更正的代币。虽然这不是一项非常复杂的任务,但我很欣赏你的回答的彻底性