我正在使用https://www.nuget.org/packages/Paseto.Core/這就是我生成 PASETO 令牌的方式:
public async Task<TokenResponse> GenerateAsync(Client client, TokenRequest tokenRequest, string issuer, string audience)
{
var ed25519pkcs8 = await File.ReadAllTextAsync("private.pem");
var privatePemReader = new PemReader(new StringReader(ed25519pkcs8));
var ed25519pkcs8Parameters = (Ed25519PrivateKeyParameters)privatePemReader.ReadObject();
ISigner signer = new Ed25519Signer();
signer.Init(true, ed25519pkcs8Parameters);
var pasetoToken = new PasetoBuilder()
.Use(ProtocolVersion.V4, Purpose.Public)
.WithKey(signer.GenerateSignature(), Encryption.AsymmetricSecretKey)
.Issuer(issuer)
.Subject(tokenRequest.ClientId)
.Audience(audience)
.NotBefore(DateTime.UtcNow)
.IssuedAt(DateTime.UtcNow)
.Expiration(DateTime.UtcNow.AddSeconds(client.AccessTokenLifetime))
.TokenIdentifier(Guid.NewGuid().ToString())
.AddClaim("client_id", tokenRequest.ClientId)
.AddClaim("scopes", tokenRequest.Scopes)
.Encode();
return new TokenResponse
{
AccessToken = pasetoToken,
Lifetime = client.AccessTokenLifetime,
Scope = tokenRequest.Scopes
};
}
生成的 PASETO 令牌如下所示:v4.public.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDMyMyIsInN1YiI6InRlc3RfY3JlZGVudGlhbHMiLCJhdWQiOiJ0ZXN0QXBpUmVzb3VyY2UiLCJuYmYiOiIyMDIyLTA1LTA3VDE4OjM4OjU2LjU0MjM2OTFaIiwiaWF0IjoiMjAyMi0wNS0wN1QxODozODo1Ni41NDI0MzUzWiIsImV4cCI6IjIwMjItMDUtMDdUMTk6Mzg6NTYuNTQyNDcwN1oiLCJqdGkiOiI5ODk3Mzc4Mi1kNWQwLTQzMjktYWY0ZS1kNTU3NGI4Y2Q2YmMiLCJjbGllbnRfaWQiOiJ0ZXN0X2NyZWRlbnRpYWxzIiwic2NvcGVzIjoidGVzdC5yZWFkIn0pQzMpSSXa-inBjgvDBNFgm7tE4w6J-TzzntJfKJErGRfm2ARuswWxJinhQMT-9v5q1ntyk4UtoIMr9ny0t4AH
所以我創建了一個用于驗證令牌的測驗 API,結果始終如下所示:
{
"IsValid":false,
"Paseto":null,
"Exception":{
"Expected":null,
"Received":null,
"Message":"The token signature is not valid",
"Data":{
},
"InnerException":null,
"HelpLink":null,
"Source":null,
"HResult":-2146233088,
"StackTrace":null
}
}
這就是驗證的樣子:
[HttpGet]
public IActionResult DecodePaseto([FromQuery] string token)
{
var ed25519x509 = System.IO.File.ReadAllText("public.pem");
var publicPemReader = new PemReader(new StringReader(ed25519x509));
var ed25519x509Parameters = (Ed25519PublicKeyParameters)publicPemReader.ReadObject();
var paseto = new PasetoBuilder()
.Use(ProtocolVersion.V4, Purpose.Public)
.WithKey(ed25519x509Parameters.GetEncoded(), Encryption.AsymmetricPublicKey)
.Decode(token);
return Ok(JsonConvert.SerializeObject(paseto));
}
一切似乎都很好,但存在簽名或驗證錯誤。有什么問題?
uj5u.com熱心網友回復:
Paseto 使用原始公鑰(32 位元組)和原始私鑰和原始公鑰的串聯(32 位元組 32 位元組 = 64 位元組)作為秘密密鑰,請參閱此處了解秘密 Ed25519 密鑰的不同格式的說明。
雖然在問題的發布代碼中正確匯入了公鑰,但作為私鑰,使用了使用空字串的私鑰生成的 Ed25519 簽名。這是不正確的,但有效(在沒有引發例外的意義上),因為簽名的大小為 64 位元組,與密鑰的長度相同。當然,驗證失敗。
以下代碼顯示了 Paseto 密鑰的正確構造。為簡單起見,應用了 Linq,但也Buffer.BlockCopy()可以使用例如:
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using System;
using System.IO;
using System.Linq;
...
string ed25519pkcs8 = @"-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIAYIsKL0xkTkAXDhUN6eDheqODEOGyFZ04jsgFNCFxZf
-----END PRIVATE KEY-----";
PemReader privatePemReader = new PemReader(new StringReader(ed25519pkcs8));
Ed25519PrivateKeyParameters ed25519pkcs8Parameters = (Ed25519PrivateKeyParameters)privatePemReader.ReadObject();
string ed25519x509 = @"-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA3mcwgf2DrWLR3mQ6l2d59bGU6qUStwQrln2 rKlKxoA=
-----END PUBLIC KEY-----";
PemReader publicPemReader = new PemReader(new StringReader(ed25519x509));
Ed25519PublicKeyParameters ed25519x509Parameters = (Ed25519PublicKeyParameters)publicPemReader.ReadObject();
byte[] publicKey = ed25519x509Parameters.GetEncoded(); // raw 32 bytes public key
byte[] secretKey = ed25519pkcs8Parameters.GetEncoded().Concat(publicKey).ToArray(); // raw 32 bytes private key raw 32 bytes public key
測驗:
使用上述密鑰,簽名是可行的,如下(使用任意測驗資料):
using Paseto;
using Paseto.Builder;
...
string pasetoToken = new PasetoBuilder()
.Use(ProtocolVersion.V4, Purpose.Public)
.WithSecretKey(secretKey) // short for .WithKey(secretKey, Encryption.AsymmetricSecretKey)
.Subject("subject")
.Issuer("whoever")
.Audience("https://www.whatever.com/someurl")
.NotBefore(DateTime.UtcNow)
.IssuedAt(DateTime.UtcNow)
.Expiration(DateTime.UtcNow.AddSeconds(3600))
.TokenIdentifier(Guid.NewGuid().ToString())
.AddClaim("client_id", "client_id")
.AddClaim("scopes", "scopes")
.Encode();
Console.WriteLine(pasetoToken);
并使用上述公鑰進行驗證:
using Paseto;
using Paseto.Builder;
...
PasetoTokenValidationParameters validationParameters = new PasetoTokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = "whoever",
ValidateAudience = true,
ValidAudience = "https://www.whatever.com/someurl"
};
PasetoTokenValidationResult paseto = new PasetoBuilder()
.Use(ProtocolVersion.V4, Purpose.Public)
.WithPublicKey(publicKey) // short for .WithKey(publicKey, Encryption.AsymmetricPublicKey)
.Decode(pasetoToken, validationParameters);
Console.WriteLine(paseto.IsValid ? paseto.Paseto.RawPayload : "Decoding failed");
整個代碼的可能輸出是:
v4.public.eyJzdWIiOiJzdWJqZWN0IiwiaXNzIjoid2hvZXZlciIsImF1ZCI6Imh0dHBzOi8vd3d3LndoYXRldmVyLmNvbS9zb21ldXJsIiwibmJmIjoiMjAyMi0wNS0wN1QyMjowNzo0NS4yNzA1NjU4WiIsImlhdCI6IjIwMjItMDUtMDdUMjI6MDc6NDUuMjcwNjQ1OVoiLCJleHAiOiIyMDIyLTA1LTA3VDIzOjA3OjQ1LjI3MDY4NzRaIiwianRpIjoiNDU0MWI2NmMtOGRlZi00Mjg3LWFmZGMtYTE3ZDNhMDY3NjYxIiwiY2xpZW50X2lkIjoiY2xpZW50X2lkIiwic2NvcGVzIjoic2NvcGVzIn1RyxW-gjy6va7IA5pL9pZMqcrBjYkYFX16AV7IqTt5Fa5YtQMbIJQkfu24uq7bR2lx0WMLHa0xr2fsJRtdpsAG
{"sub":"subject","iss":"whoever","aud":"https://www.whatever.com/someurl","nbf":"2022-05-07T22:07:45.2705658Z","iat":"2022-05-07T22:07:45.2706459Z","exp":"2022-05-07T23:07:45.2706874Z","jti":"4541b66c-8def-4287-afdc-a17d3a067661","client_id":"client_id","scopes":"scopes"}
這eyJz...psAG是負載的 Bas64url 編碼和串聯的 64 位元組 Ed25519 簽名。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/470026.html
上一篇:如何在C#中創建一個抽象的getter,這樣您就不必在每個孩子中宣告?
下一篇:AWSCLI|esc回傳文本
