oauth2.0 使用at_hash验证访问令牌

sxissh06  于 2023-08-02  发布在  其他
关注(0)|答案(5)|浏览(131)

我正在尝试根据at_hash验证访问令牌。令牌头是这样的
第一个月
如何从我的访问令牌获取id令牌中的Base64编码at_hash声明值?有没有一个在线工具可以帮助我解决这个问题?SHA256哈希计算器不是一个正确的工具吗?
谢啦,谢啦

z9gpfhce

z9gpfhce1#

SHA256哈希计算器不是一个正确的工具吗?
它不起作用,因为你需要在其中一个步骤中使用二进制数据,几乎所有的Web工具都期望某种文本作为输入并生成文本作为输出。在线工具并不适合这一点。我会写一个工具,这样你就可以看到它是如何完成的。
如何从我的访问令牌获取id令牌中的Base64编码at_hash声明值?
这是我的第一次C#程序迭代2:)所以如果它很丑,那是因为我以前从来没有用过它。后面的解释将解释如何计算at_hash令牌,包括为什么我们需要decode_base64

using System;
using System.Security.Cryptography;

using System.Collections.Generic;
using System.Text;
namespace AtHash
{
    class AtHash
    {
        private const String access_token = "ya29.eQGmYe6H3fP_d65AY0pOMCFikA0f4hzVZGmTPPyv7k_l6HzlEIpFXnXGZjcMhkyyuqSMtN_RTGJ-xg";
        private const String id1 = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImUxMWQ1N2QxZmY0ODA0YjMxYzA1MWI3MWY2ZDVlNWExZmQyOTdjZjgifQ";
        private const String id2 = "eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEwMTY5NDg0NDc0Mzg2Mjc2MzM0IiwiYXpwIjoiNDA3NDA4NzE4MTkyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiZW1haWwiOiJiaWxsZDE2MDBAZ21haWwuY29tIiwiYXRfaGFzaCI6ImxPdEkwQlJvdTBaNExQdFF1RThjQ3ciLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXVkIjoiNDA3NDA4NzE4MTkyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiaWF0IjoxNDMyMTQyMjIyLCJleHAiOjE0MzIxNDU4MjJ9";

        private byte[] decode_base64(String str) {
            List<byte> l = new List<Byte>(Encoding.Default.GetBytes(str));
            while (l.Count % 4 != 0 ){
                l.Add(Convert.ToByte('='));
            }
            return Convert.FromBase64String(Encoding.Default.GetString(l.ToArray()));
        }

        public String sha256_at_hash(String access_token) {
            SHA256Managed hashstring = new SHA256Managed();
            byte[] bytes         = Encoding.Default.GetBytes(access_token);
            byte[] hash = hashstring.ComputeHash(bytes);
            Byte[] sixteen_bytes = new Byte[16];
            Array.Copy(hash, sixteen_bytes, 16);
            return Convert.ToBase64String(sixteen_bytes).Trim('=');
        }

        public static void Main (string[] args) {
            AtHash ah = new AtHash();
            byte[] id1_str = ah.decode_base64 (id1);
            byte[] id2_str = ah.decode_base64 (id2);

            Console.WriteLine(Encoding.Default.GetString(id1_str));
            Console.WriteLine(Encoding.Default.GetString(id2_str));

            Console.WriteLine ("\n\tat_hash value == " + ah.sha256_at_hash(access_token));
        }
    }
}

字符串
这个程序的输出(格式化我的)

{ 
  "alg":"RS256",
  "kid":"e11d57d1ff4804b31c051b71f6d5e5a1fd297cf8"
}
{
   "exp" : 1432145822,
   "iat" : 1432142222,
   "azp" : "407408718192.apps.googleusercontent.com",
   "aud" : "407408718192.apps.googleusercontent.com",
   "email_verified" : true,
   "iss" : "accounts.google.com",
   "at_hash" : "lOtI0BRou0Z4LPtQuE8cCw",
   "sub" : "110169484474386276334",
   "email" : "billd1600@gmail.com"
}

at_hash value == lOtI0BRou0Z4LPtQuE8cCw


这是如何验证at_hash值。如果你想使用我使用过的数据,你可以跳过谷歌部分,但如果你想测试新的数据,你可以在谷歌获得它。

从Google O2Auth Playground获取访问令牌

到这里去

https://developers.google.com/oauthplayground/


不要选择任何东西,页面底部有一个输入框。键入openid并点击Authorize APIs,单击要使用的id并选择allow。选择Exchange authorization code for tokens。如果成功,你会得到类似于以下的东西。

{ 
 "access_token": "ya29.eQGmYe6H3fP_d65AY0pOMCFikA0f4hzVZGmTPPyv7k_l6HzlEIpFXnXGZjcMhkyyuqSMtN_RTGJ-xg", 
 "token_type": "Bearer", "expires_in": 3600, 
 "refresh_token": "1/r5RRN6oRChjLtY5Y_T3lrqOy7n7QZJDQUVm8ZI1xGdoMEudVrK5jSpoR30zcRFq6", 
 "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImUxMWQ1N2QxZmY0ODA0YjMxYzA1MWI3MWY2ZDVlNWExZmQyOTdjZjgifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEwMTY5NDg0NDc0Mzg2Mjc2MzM0IiwiYXpwIjoiNDA3NDA4NzE4MTkyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiZW1haWwiOiJiaWxsZDE2MDBAZ21haWwuY29tIiwiYXRfaGFzaCI6ImxPdEkwQlJvdTBaNExQdFF1RThjQ3ciLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXVkIjoiNDA3NDA4NzE4MTkyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiaWF0IjoxNDMyMTQyMjIyLCJleHAiOjE0MzIxNDU4MjJ9.jtnP4Ffw2bPjfxRAEvHI8j88YBI4OJrw2BU7AQUCP2AUOKRC5pxwVn3vRomGTKiuMbnHqMyMiVSQZWTjAgjQrmaANxTEA68UMKh3dtu63hh4LHkGJly2hFcIKwbHxMWPDRO9nv8LxAUeCF5ccMgFNXhu-i-CeVtrMOsjCq6j5Qc"
}


id_token分为三个部分,使用句点.分开。前两部分是base64编码的。我忽略了id_token的第三部分。我们需要用base64解码这两个。注意,我使用Perl是为了避免必须填充base64字符串,即Perl为我们处理它。
你已经知道的第一部分给出了我们需要使用的算法。

perl -MMIME::Base64 -e 'print decode_base64("eyJhbGciOiJSUzI1NiIsImtpZCI6ImUxMWQ1N2QxZmY0ODA0YjMxYzA1MWI3MWY2ZDVlNWExZmQyOTdjZjgifQ")'
{
 "alg":"RS256",
 "kid":"e11d57d1ff4804b31c051b71f6d5e5a1fd297cf8"
}


第二部分给出的是at_hash值。

perl -MMIME::Base64 -e 'print decode_base64("eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEwMTY5NDg0NDc0Mzg2Mjc2MzM0IiwiYXpwIjoiNDA3NDA4NzE4MTkyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiZW1haWwiOiJiaWxsZDE2MDBAZ21haWwuY29tIiwiYXRfaGFzaCI6ImxPdEkwQlJvdTBaNExQdFF1RThjQ3ciLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXVkIjoiNDA3NDA4NzE4MTkyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiaWF0IjoxNDMyMTQyMjIyLCJleHAiOjE0MzIxNDU4MjJ9")'

{
"iss":"accounts.google.com",
........
"at_hash":"lOtI0BRou0Z4LPtQuE8cCw",
........
"exp":1432145822
}


现在我们知道了at_hash的值是什么,我们可以使用access_token来验证它们是否相同。下面的Perl程序就是这样做的。

#!/usr/bin/env perl
use strict;
use warnings;
use MIME::Base64;
use Digest::SHA qw(sha256);
my $data = "ya29.eQGmYe6H3fP_d65AY0pOMCFikA0f4hzVZGmTPPyv7k_l6HzlEIpFXnXGZjcMhkyyuqSMtN_RTGJ-xg"; 
my $digest = sha256($data);
my $first_16_bytes = substr($digest,0,16);
print encode_base64($first_16_bytes);


这个程序可以如下运行

perl sha256.pl 
lOtI0BRou0Z4LPtQuE8cCw==


注意,我们得到了at_hash,但为什么它们不一样...,它们实际上是一样的,只是其中一个缺少了填充。添加=符号,直到以下结果成立为止。

(strlen($base64_string) % 4 == 0)


在我们的情况下

strlen("lOtI0BRou0Z4LPtQuE8cCw") == 22


所以我们得到了两个==添加到结果中:)。它们不在令牌中的原因是因为编写规范的人不相信通过网络传递不必要的字节是一个好主意,如果它们可以在另一端添加。

bweufnob

bweufnob2#

它在规范中被描述确切地
https://openid.net/specs/openid-connect-core-1_0.html
3.1.3.6. ID令牌
at_hash可选。访问令牌哈希值。它的值是access_token值的ASCII表示的八位字节散列的最左边一半的base64 url编码,其中使用的散列算法是ID令牌的JOSE头的alg头参数中使用的散列算法。例如,如果alg是RS 256,用SHA-256散列access_token值,然后取最左边的128位,base64 url对其进行编码。at_hash值是区分大小写的字符串。

mqkwyuun

mqkwyuun3#

我在生成客户机密时遇到了一个类似的问题。
查看IdentityServer使用的HashExtensions类很有帮助;在我的例子中,我没有得到UTF8编码的字节。我怀疑你链接的在线工具采用了不同的方法将字节数组编码为字符串。

1tuwyuhd

1tuwyuhd4#

对于Perl,上面的代码需要像这样扩展:

#!/usr/bin/env perl
use strict;
use warnings;
use MIME::Base64;
use Digest::SHA;
my $access_token = "SOMETHING"; 
my $digest = Digest::SHA::sha256( $access_token );
my $first_16_bytes = substr( $digest, 0, 16 );
print MIME::Base64::encode_base64url( $first_16_bytes );

字符串
那么,它实际上是符合标准的。
确保将MIME::Base64模块升级到最新版本。

ctehm74n

ctehm74n5#

Golang溶液

func verifyAtHash(accessToken string, atHash string) bool {
    h := sha256.New() // for RS256, ES256, PS256
    h.Write([]byte(accessToken)) // hash documents that Write never return an error
    sum := h.Sum(nil)[:h.Size()/2] // left-most 128 bits
    atHashFromAccessToken := base64.RawURLEncoding.EncodeToString(sum)
    return atHashFromAccessToken == atHash
}

字符串
这里使用不同的签名算法更完整的解决方案(参见verifyHashClaim):https://github.com/hashicorp/cap/blob/v0.3.1/oidc/id_token.go#L95

相关问题