/ GUNRPG.Infrastructure / Security / ServerCertificate.cs
ServerCertificate.cs
 1  namespace GUNRPG.Security;
 2  
 3  public sealed class ServerCertificate
 4  {
 5      private readonly byte[] _publicKey;
 6      private readonly byte[] _signature;
 7  
 8      public ServerCertificate(
 9          Guid serverId,
10          byte[] publicKey,
11          DateTimeOffset issuedAt,
12          DateTimeOffset validUntil,
13          byte[] signature)
14      {
15          if (validUntil <= issuedAt)
16          {
17              throw new ArgumentOutOfRangeException(nameof(validUntil), "Certificate expiry must be after issuance.");
18          }
19  
20          ServerId = serverId;
21          _publicKey = AuthorityCrypto.CloneAndValidatePublicKey(publicKey);
22          IssuedAt = issuedAt;
23          ValidUntil = validUntil;
24          _signature = AuthorityCrypto.CloneAndValidateSignature(signature);
25      }
26  
27      public Guid ServerId { get; }
28  
29      public byte[] PublicKey => (byte[])_publicKey.Clone();
30  
31      internal byte[] PublicKeyBytes => _publicKey;
32  
33      public DateTimeOffset IssuedAt { get; }
34  
35      public DateTimeOffset ValidUntil { get; }
36  
37      public byte[] Signature => (byte[])_signature.Clone();
38  
39      internal byte[] SignatureBytes => _signature;
40  
41      internal byte[] ComputePayloadHash() =>
42          AuthorityCrypto.ComputeCertificatePayloadHash(ServerId, _publicKey, IssuedAt, ValidUntil);
43  
44      internal static ServerCertificate Create(
45          Guid serverId,
46          byte[] publicKey,
47          DateTimeOffset issuedAt,
48          DateTimeOffset validUntil,
49          byte[] rootPrivateKey)
50      {
51          var payloadHash = AuthorityCrypto.ComputeCertificatePayloadHash(serverId, publicKey, issuedAt, validUntil);
52          var signature = AuthorityCrypto.SignHashedPayload(rootPrivateKey, payloadHash);
53          return new ServerCertificate(serverId, publicKey, issuedAt, validUntil, signature);
54      }
55  }