5-improve-encrypted-storage (#6)
Added the use of DEK's for encryption of secrets. Both the KEK's and DEK's are stored in a way that you can have multiple key of which one is active. But the others are still available for decrypting. This allows for implementing key rotation. Co-authored-by: eelke <eelke@eelkeklein.nl> Co-authored-by: Eelke76 <31384324+Eelke76@users.noreply.github.com> Reviewed-on: #6
This commit is contained in:
parent
138f335af0
commit
07393f57fc
87 changed files with 1903 additions and 533 deletions
|
|
@ -1,11 +1,29 @@
|
|||
using IdentityShroud.Core.Security;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IdentityShroud.Core.Model;
|
||||
|
||||
[Table("client")]
|
||||
[Index(nameof(ClientId), IsUnique = true)]
|
||||
public class Client
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
public Guid RealmId { get; set; }
|
||||
[MaxLength(40)]
|
||||
public required string ClientId { get; set; }
|
||||
[MaxLength(80)]
|
||||
public string? Name { get; set; }
|
||||
[MaxLength(2048)]
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? SignatureAlgorithm { get; set; } = JsonWebAlgorithm.RS256;
|
||||
[MaxLength(20)]
|
||||
public string? SignatureAlgorithm { get; set; }
|
||||
|
||||
public bool AllowClientCredentialsFlow { get; set; } = false;
|
||||
|
||||
public required DateTime CreatedAt { get; set; }
|
||||
|
||||
public List<ClientSecret> Secrets { get; set; } = [];
|
||||
}
|
||||
17
IdentityShroud.Core/Model/ClientSecret.cs
Normal file
17
IdentityShroud.Core/Model/ClientSecret.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Security;
|
||||
|
||||
namespace IdentityShroud.Core.Model;
|
||||
|
||||
[Table("client_secret")]
|
||||
public class ClientSecret
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
public Guid ClientId { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? RevokedAt { get; set; }
|
||||
public required EncryptedValue Secret { get; set; }
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
|
||||
namespace IdentityShroud.Core.Model;
|
||||
|
||||
|
||||
[Table("key")]
|
||||
public class Key
|
||||
{
|
||||
private byte[] _privateKeyDecrypted = [];
|
||||
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? DeactivatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Key with highest priority will be used. While there is not really a use case for this I know some users
|
||||
/// are more comfortable replacing keys by using priority then directly deactivating the old key.
|
||||
/// </summary>
|
||||
public int Priority { get; set; } = 10;
|
||||
|
||||
public byte[] PrivateKeyEncrypted
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
_privateKeyDecrypted = [];
|
||||
}
|
||||
} = [];
|
||||
|
||||
public byte[] GetPrivateKey(IEncryptionService encryptionService)
|
||||
{
|
||||
if (_privateKeyDecrypted.Length == 0 && PrivateKeyEncrypted.Length > 0)
|
||||
_privateKeyDecrypted = encryptionService.Decrypt(PrivateKeyEncrypted);
|
||||
return _privateKeyDecrypted;
|
||||
}
|
||||
|
||||
public void SetPrivateKey(IEncryptionService encryptionService, byte[] privateKey)
|
||||
{
|
||||
PrivateKeyEncrypted = encryptionService.Encrypt(privateKey);
|
||||
_privateKeyDecrypted = privateKey;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using IdentityShroud.Core.Security;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IdentityShroud.Core.Model;
|
||||
|
||||
|
|
@ -20,11 +19,22 @@ public class Realm
|
|||
public string Name { get; set; } = "";
|
||||
public List<Client> Clients { get; init; } = [];
|
||||
|
||||
public List<Key> Keys { get; init; } = [];
|
||||
public List<RealmKey> Keys { get; init; } = [];
|
||||
|
||||
public List<RealmDek> Deks { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Can be overriden per client
|
||||
/// </summary>
|
||||
public string DefaultSignatureAlgorithm { get; set; } = JsonWebAlgorithm.RS256;
|
||||
|
||||
}
|
||||
|
||||
[Table("realm_dek")]
|
||||
public record RealmDek
|
||||
{
|
||||
public required DekId Id { get; init; }
|
||||
public required bool Active { get; set; }
|
||||
public required string Algorithm { get; init; }
|
||||
public required EncryptedDek KeyData { get; init; }
|
||||
public required Guid RealmId { get; init; }
|
||||
}
|
||||
|
|
|
|||
27
IdentityShroud.Core/Model/RealmKey.cs
Normal file
27
IdentityShroud.Core/Model/RealmKey.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Security;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IdentityShroud.Core.Model;
|
||||
|
||||
|
||||
[Table("realm_key")]
|
||||
public record RealmKey
|
||||
{
|
||||
public required Guid Id { get; init; }
|
||||
public required string KeyType { get; init; }
|
||||
|
||||
|
||||
public required EncryptedDek Key { get; init; }
|
||||
public required DateTime CreatedAt { get; init; }
|
||||
public DateTime? RevokedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Key with highest priority will be used. While there is not really a use case for this I know some users
|
||||
/// are more comfortable replacing keys by using priority then directly deactivating the old key.
|
||||
/// </summary>
|
||||
public int Priority { get; set; } = 10;
|
||||
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue