C#でRS256(非対称)を使用してJWTを確認する
非対称RS256を使用しているが、「SymmetricSecurityKey()」があるために失敗していると思われるこのようなコードがいくつかあります。トークンはから手作業で生成されましたhttps://jwt.io/
- 非対称公開鍵を使用するようにこれを変換するにはどうすればよいですか?
- また、私はC#を初めて使用し、dotnet標準をターゲットにしたいので、間違ったライブラリを使用していないかどうかも疑問に思っています。(私はプレビューリリースに依存しています)
λ cat Program.cs
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using System.Linq;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
namespace jwttest
{
class Program
{
static void Main(string[] args)
{
string jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA";
var pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB";
var rawKey = Encoding.ASCII.GetBytes(pubKey);
var tokenHandler = new JwtSecurityTokenHandler();
// var rsa = ?
tokenHandler.ValidateToken(jwt, new TokenValidationParameters {
IssuerSigningKey = new SymmetricSecurityKey(rawKey)
},
out SecurityToken validatedToken);
}
}
}
C:\src\jwttest (cgt-test-5 -> origin)
λ dotnet run
[2020-08-18T23:41:05.7108585-07:00 Info] raw=System.Byte[] [392]
Unhandled exception. Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10503: Signature validation failed. Keys tried: 'System.Text.StringBuilder'.
Exceptions caught:
'System.Text.StringBuilder'.
token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'.
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at jwttest.Program.Main(String[] args) in C:\src\jwttest\Program.cs:line 22
λ cat jwttest.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- Using preview release because it only depends on dotnet standard. Prior versions need framework. -->
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.7.2-preview-10803222715" />
</ItemGroup>
</Project>
λ cat jwt.json
{
"alg": "RS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}
回答
最初の質問について:
投稿されたスタックトレースによると、.NET Core3.1を使用しているようです。これにより、次のように公開X.509 / SPKIキーを簡単にインポートできます。var pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB"; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(pubKey), out _); // import the public X.509/SPKI DER encoded key
ImportSubjectPublicKeyInfo() .NET Core3.0以降で使用できます。
編集開始:以前のバージョンの.NET Core(3.0より前)または.NET FrameworkImportSubjectPublicKeyInfo()では使用できないため、少なくとも.NET Standard2.1が必要です。
以前のバージョン、たとえば.NET Standard 2.0の場合、1つの可能性は、BouncyCastle、より正確にはその
Org.BouncyCastle.OpenSsl.PemReader
クラスを使用することです。これにより、公開鍵をX509 / SPKI形式(および、PKCS#1形式でも)でインポートできます。では、この答えは、使用する方法の例を見つけるでしょうPemReader
。PemReader
名前が示すように、プロセスは、PEMエンコーディング、つまりDERエンコーディングへの変換(つまり、ヘッダー、フッター、改行の削除、および残りのBase64デコード)を実行してImportSubjectPublicKeyInfo()
はなりません。またPemReader
、ヘッダー(-----BEGIN PUBLIC KEY-----\n
)の直後に少なくとも1つの改行があり、フッター(\n-----END PUBLIC KEY-----
)の直前に2つ目の改行が必要であることに注意してくださいPemReader
。この場合、64文字ごとにBase64でエンコードされた本文の改行はオプションです。もう1つの可能性は、メソッドを提供するパッケージopensslkeyです。このメソッドは
opensslkey.DecodeX509PublicKey()
、に類似したDERエンコーディングでX509 / SPKIキーを処理できますImportSubjectPublicKeyInfo
。編集終了2番目の質問について:
いくつかの.NET標準バージョンがあります。たとえば、.NET Core3.0は.NETStandard2.1を実装しています。使用しているパッケージSystem.IdentityModel.Tokens.Jwt6.7.2 -preview-10803222715には、.NET Standard2.0が必要です。System.IdentityModel.Tokens.Jwt
JSON Web Token(JWT)の作成と検証をサポートするパッケージです。投稿されたトークンの場合、検証は次のように実装できます。string jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA"; var tokenHandler = new JwtSecurityTokenHandler(); bool verified = false; try { tokenHandler.ValidateToken(jwt, new TokenValidationParameters { ValidateAudience = false, ValidateLifetime = false, ValidateIssuer = false, IssuerSigningKey = new RsaSecurityKey(rsa) }, out _); verified = true; } catch { verified = false; } Console.WriteLine("Verified: " + verified);
検証は、検証パラメーター、つまりの2番目のパラメーターを介して制御できますValidateToken()。投稿されたトークンにはクレーム iss、aud、expが含まれていないため、これは次のように確認できます。https://jwt.io/)、これらは私の例では検証から除外されています。
チュートリアル「ASP.NETCoreでのJWTトークンの作成と検証」では、特に「トークンの検証」の章で、より詳細な説明を見つけることができます。
ValidateToken()
基本的に、JWT署名の検証プロセスをカプセル化します。A JWTは、 3つの部分から構成されたデータ構造である:ヘッダ、ペイロード及び署名、Base64urlある個々の部品は、符号化されたドットによって互いに分離します。
署名は、さまざまなアルゴリズムを使用して作成されます。たとえば、RS256の場合、データ(Base64urlでエンコードされたヘッダーと区切り文字を含むペイロード)は、PKCS#1v1.5パディングとダイジェストSHA256を使用したアルゴリズムRSAを使用して署名されます。
トークンの検証は、署名の検証に対応します。これは、暗号化APIのみを使用して(つまり、System.IdentityModel.Tokens.Jwtを使用せずに)実行できます。これは、リンクされた質問の受け入れられた回答で実行されます。 @zaitsmanのコメント。