質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JWT(JSON Web Token)

JWT(JSON Web Token)とは、JSONをベースとしたアクセストークンの仕様。電子署名付きのURL safeなJSONのことを指します。電子署名が付いているため、改ざんをチェックできる点がメリットです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

1回答

1401閲覧

JwtSecurityToken.Header["x5c"]を「Newtonsoft.Json.Linq.JArray」にキャストできません

aiueoao

総合スコア146

JWT(JSON Web Token)

JWT(JSON Web Token)とは、JSONをベースとしたアクセストークンの仕様。電子署名付きのURL safeなJSONのことを指します。電子署名が付いているため、改ざんをチェックできる点がメリットです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2021/08/14 09:24

編集2021/08/14 11:26

前提・実現したいこと

SafetynetのJWT検証をするためにこちらのSampleを試しているのですが、JwtSecurityTokenに埋め込まれたX509セキュリティキーを取得する箇所でNullReferenceExceptionエラーが出ます

c#

1keys = (token.Header["x5c"] as JArray)

なぜキャストが失敗するのかがわかりません.
Microsoft.IdentityModel.Json.Linq.Jarrray」タイプのオブジェクトを「Newtonsoft.Json.Linq.Jarrray」タイプにキャストしたいです.

検証する対象のJWTはjwt.ioにて確認したところ特に問題はないと思います.

HEADER:ALGORITHM & TOKEN TYPE

{
"alg": "RS256",
"x5c": [ "文字列1",
"文字列2",
"文字列3"
]
}

コード側でtoken.Header["x5c"].ToString()としてみると上記と同じ
[ "文字列1",
"文字列2",
"文字列3"
]
がとれます.

発生している問題・エラーメッセージ

エラーメッセージ { "errorType": "NullReferenceException", "errorMessage": "Object reference not set to an instance of an object.", "stackTrace": [ "at AWSLambdaSafetyNetVerify.OfflineVerify.GetEmbeddedKeys(JwtSecurityToken token) in D:\Myfolder\Documents\SafetyNetCsharpLambda\VisualStudioProject\AWSLambdaSafetyNetVerify\OfflineVerify.cs:line 172", "at AWSLambdaSafetyNetVerify.OfflineVerify.ParseAndVerify(String signedAttestationStatement) in D:\Myfolder\Documents\SafetyNetCsharpLambda\VisualStudioProject\AWSLambdaSafetyNetVerify\OfflineVerify.cs:line 57", "at AWSLambdaSafetyNetVerify.Function.SafetyNetAnalysisSignature(String jwt) in D:\Myfolder\Documents\SafetyNetCsharpLambda\VisualStudioProject\AWSLambdaSafetyNetVerify\Function.cs:line 119", "at AWSLambdaSafetyNetVerify.Function.MyMethod(Object input) in D:\Myfolder\Documents\SafetyNetCsharpLambda\VisualStudioProject\AWSLambdaSafetyNetVerify\Function.cs:line 54", "at lambda_method(Closure , Stream , Stream , LambdaContextInternal )" ]

該当のソースコード

c#

1using Microsoft.IdentityModel.Tokens; 2using Newtonsoft.Json.Linq; 3using System; 4using System.Collections.Generic; 5using System.IdentityModel.Tokens.Jwt; 6using System.Linq; 7using System.Security.Cryptography; 8using System.Security.Cryptography.X509Certificates; 9 10namespace SafetyNetCheck 11{ 12 /// <summary> 13 /// Sample code to verify the device attestation offline. 14 /// </summary> 15 public static class OfflineVerify 16 { 17 /// <summary> 18 /// Parses and verifies a SafetyNet attestation statement. 19 /// </summary> 20 /// <param name="signedAttestationStatement">A string containing the 21 /// JWT attestation statement.</param> 22 /// <returns>A parsed attestation statement. null if the statement is 23 /// invalid.</returns> 24 public static AttestationStatement ParseAndVerify( 25 string signedAttestationStatement) 26 { 27 // First parse the token and get the embedded keys. 28 JwtSecurityToken token; 29 try 30 { 31 token = new JwtSecurityToken(signedAttestationStatement); 32 } 33 catch (ArgumentException) 34 { 35 // The token is not in a valid JWS format. 36 return null; 37 } 38 39 // We just want to validate the authenticity of the certificate. 40 var validationParameters = new TokenValidationParameters 41 { 42 ValidateIssuer = false, 43 ValidateAudience = false, 44 ValidateLifetime = false, 45 ValidateIssuerSigningKey = true, 46 IssuerSigningKeys = GetEmbeddedKeys(token) 47 }; 48 49 // Perform the validation 50 var tokenHandler = new JwtSecurityTokenHandler(); 51 SecurityToken validatedToken; 52 try 53 { 54 tokenHandler.ValidateToken( 55 signedAttestationStatement, 56 validationParameters, 57 out validatedToken); 58 } 59 catch (ArgumentException) 60 { 61 // Signature validation failed. 62 return null; 63 } 64 65 // Verify the hostname 66 if (!(validatedToken.SigningKey is X509SecurityKey)) 67 { 68 // The signing key is invalid. 69 return null; 70 } 71 if (!VerifyHostname( 72 "attest.android.com", 73 validatedToken.SigningKey as X509SecurityKey)) 74 { 75 // The certificate isn't issued for the hostname 76 // attest.android.com. 77 return null; 78 } 79 80 // Parse and use the data JSON. 81 Dictionary<string, string> claimsDictionary = token.Claims 82 .ToDictionary(x => x.Type, x => x.Value); 83 84 return new AttestationStatement(claimsDictionary); 85 } 86 87 /// <summary> 88 /// Verifes an X509Security key, and checks that it is issued for a 89 /// given hostname. 90 /// </summary> 91 /// <param name="hostname">The hostname to check to.</param> 92 /// <param name="securityKey">The security key to verify.</param> 93 /// <returns>true if securityKey is valid and is issued to the given 94 /// hostname.</returns> 95 private static bool VerifyHostname( 96 string hostname, 97 X509SecurityKey securityKey) 98 { 99 string commonName; 100 try 101 { 102 // Verify the certificate with Verify(). Alternatively, you 103 // could use the commented code below instead of Verify(), to 104 // get more details of why a particular verification failed. 105 // 106 // var chain = new X509Chain(); 107 // var chainBuilt = chain.Build(securityKey.Certificate); 108 // if (!chainBuilt) 109 // { 110 // string s; // One could use a StringBuilder instead. 111 // foreach (X509ChainStatus chainStatus in 112 // chain.ChainStatus) 113 // { 114 // s += string.Format( 115 // "Chain error: {0} {1}\n", 116 // chainStatus.Status, 117 // chainStatus.StatusInformation); 118 // } 119 // } 120 if (!securityKey.Certificate.Verify()) 121 { 122 return false; 123 } 124 125 commonName = securityKey.Certificate.GetNameInfo( 126 X509NameType.DnsName, false); 127 } 128 catch (CryptographicException) 129 { 130 return false; 131 } 132 return (commonName == hostname); 133 } 134 135 /// <summary> 136 /// Retrieves the X509 security keys embedded in a JwtSecurityToken. 137 /// </summary> 138 /// <param name="token">The token where the keys are to be retrieved 139 /// from.</param> 140 /// <returns>The embedded security keys. null if there are no keys in 141 /// the security token.</returns> 142 /// <exception cref="KeyNotFoundException">Thrown when the JWT data 143 /// does not contain a valid signature 144 /// header "x5c".</exception> 145 /// <exception cref="CryptographicException">Thrwon when the JWT data 146 /// does not contain valid signing 147 /// keys.</exception> 148 private static X509SecurityKey[] GetEmbeddedKeys( 149 JwtSecurityToken token) 150 { 151 // The certificates are embedded in the "x5c" part of the header. 152 // We extract them, parse them, and then create X509 keys out of 153 // them. 154 X509SecurityKey[] keys = null; 155 keys = (token.Header["x5c"] as JArray) //<=========ここでエラー. キャストできない為 nullになる 156 .Values<string>() 157 .Select(x => new X509SecurityKey( 158 new X509Certificate2(Convert.FromBase64String(x)))) 159 .ToArray(); 160 return keys; 161 } 162 } 163} 164

補足情報(FW/ツールのバージョンなど)

Windows10 64bit
Microsoft Visual Studio Community 2019 Version 16.10.4
Newtonsoft.Json 13.0.1
System.IdentityModel.Tokens.Jwt 6.12.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

keys = (token.Header["x5c"] as JArray)
なぜキャストが失敗するのかがわかりません.

キャストが失敗しているわけではありません。token が null だからだと思いますよ。as 句で JArray にキャストできなければ keys が null になりますが、NullReferenceException にはなりません。

【追記】

JSON 文字列を取得できるなら、それを Newtonsoft.Json を使ってデシリアライズして、以下のようにパースしてみてはいかがですか? 具体例は以下の記事が参考になりませんか。 

Json.NET の JToken をパース
http://surferonwww.info/BlogEngine/post/2021/02/07/parse-jtoken-object-deserialized-by-deserializeobject-method-of-newtonsoftjson.aspx

投稿2021/08/14 11:53

編集2021/08/14 12:22
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aiueoao

2021/08/14 12:40

回答ありがとうございます! 教えて頂いた通りデシリアイズしてパースしたら通りました! private static X509SecurityKey[] GetEmbeddedKeys(JwtSecurityToken token) { X509SecurityKey[] keys = null; JToken jtoken = JsonConvert.DeserializeObject<JToken>(token.Header["x5c"].ToString()); Parse(0, jtoken); 略 }} ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問