forked from xiongyuxing/tiku-backend.net
82 lines
2.5 KiB
C#
82 lines
2.5 KiB
C#
namespace Tiku.Application.Security;
|
|
|
|
public sealed class JwtOptions
|
|
{
|
|
[System.ComponentModel.DataAnnotations.Required]
|
|
public string Issuer { get; set; } = "tiku-backend";
|
|
|
|
[System.ComponentModel.DataAnnotations.Required]
|
|
public string Audience { get; set; } = "tiku-api";
|
|
|
|
[System.ComponentModel.DataAnnotations.Required]
|
|
public string KeyId { get; set; } = "development-ephemeral";
|
|
|
|
public string PrivateKeyPem { get; set; } = string.Empty;
|
|
|
|
public Dictionary<string, string> PublicKeys { get; set; } = new(StringComparer.Ordinal);
|
|
|
|
[System.ComponentModel.DataAnnotations.Range(1, 1440)]
|
|
public int AccessTokenMinutes { get; set; } = 15;
|
|
|
|
[System.ComponentModel.DataAnnotations.Range(1, 365)]
|
|
public int RefreshTokenDays { get; set; } = 30;
|
|
|
|
public static bool BeValid(JwtOptions options, bool isProduction)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(options.Issuer) ||
|
|
string.IsNullOrWhiteSpace(options.Audience) ||
|
|
string.IsNullOrWhiteSpace(options.KeyId) ||
|
|
options.AccessTokenMinutes != 15 ||
|
|
(isProduction && string.Equals(
|
|
options.KeyId,
|
|
"development-ephemeral",
|
|
StringComparison.Ordinal)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(options.PrivateKeyPem))
|
|
{
|
|
if (isProduction)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (!IsValidRsaPem(options.PrivateKeyPem, requirePrivateKey: true))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return options.PublicKeys.All(pair =>
|
|
!string.IsNullOrWhiteSpace(pair.Key) &&
|
|
!string.Equals(pair.Key, options.KeyId, StringComparison.Ordinal) &&
|
|
IsValidRsaPem(pair.Value, requirePrivateKey: false));
|
|
}
|
|
|
|
private static bool IsValidRsaPem(string pem, bool requirePrivateKey)
|
|
{
|
|
try
|
|
{
|
|
using var rsa = System.Security.Cryptography.RSA.Create();
|
|
rsa.ImportFromPem(pem);
|
|
if (rsa.KeySize < 2048)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (requirePrivateKey)
|
|
{
|
|
_ = rsa.ExportParameters(includePrivateParameters: true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (Exception exception) when (
|
|
exception is ArgumentException or
|
|
System.Security.Cryptography.CryptographicException)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|