From 5ed8635f9c539d4c3b5f245affb276e7771ff314 Mon Sep 17 00:00:00 2001 From: Conner Turnbull Date: Mon, 9 Jun 2025 15:35:42 -0400 Subject: [PATCH] Moved the remaining methods out of `BaseLicense`. Renamed `GetDataBytes` to `ToByteArray` --- .../Licenses/Extensions/LicenseExtensions.cs | 25 ++++++++++++++++--- src/Core/Models/Business/BaseLicense.cs | 20 +++------------ src/Core/Models/Business/ILicense.cs | 6 ++--- .../Models/Business/OrganizationLicense.cs | 13 +++------- src/Core/Models/Business/UserLicense.cs | 15 +++-------- 5 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/Core/Billing/Licenses/Extensions/LicenseExtensions.cs b/src/Core/Billing/Licenses/Extensions/LicenseExtensions.cs index 727b6c6f09..fc6148a609 100644 --- a/src/Core/Billing/Licenses/Extensions/LicenseExtensions.cs +++ b/src/Core/Billing/Licenses/Extensions/LicenseExtensions.cs @@ -14,18 +14,37 @@ namespace Bit.Core.Billing.Licenses.Extensions; public static class LicenseExtensions { - public static byte[] ComputeHash(this ILicense license) => SHA256.HashData(license.GetDataBytes(true)); + public static byte[] ComputeHash(this ILicense license) => SHA256.HashData(license.ToByteArray(true)); public static bool VerifySignature(this ILicense license, X509Certificate2 certificate) { - var dataBytes = license.GetDataBytes(); + var dataBytes = license.ToByteArray(); var signatureBytes = Convert.FromBase64String(license.Signature); using var rsa = certificate.GetRSAPublicKey(); + return rsa.VerifyData(dataBytes, signatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } - public static byte[] GetDataBytesWithAttributes(this ILicense license, bool forHash = false) + public static byte[] Sign(this ILicense license, X509Certificate2 certificate) { + if (!certificate.HasPrivateKey) + { + throw new InvalidOperationException("You don't have the private key!"); + } + + var dataBytes = license.ToByteArray(); + using var rsa = certificate.GetRSAPrivateKey(); + + return rsa.SignData(dataBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } + + public static byte[] ToByteArray(this ILicense license, bool forHash = false) + { + if (!license.ValidLicenseVersion) + { + throw new NotSupportedException($"Version {license.Version} is not supported."); + } + var props = license.GetType() .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => diff --git a/src/Core/Models/Business/BaseLicense.cs b/src/Core/Models/Business/BaseLicense.cs index 9defe49adf..8645d97182 100644 --- a/src/Core/Models/Business/BaseLicense.cs +++ b/src/Core/Models/Business/BaseLicense.cs @@ -1,5 +1,4 @@ -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; +using System.Text.Json.Serialization; using Bit.Core.Billing.Licenses.Attributes; using Bit.Core.Enums; @@ -47,18 +46,7 @@ public abstract class BaseLicense : ILicense [LicenseIgnore] public string Token { get; set; } - public abstract byte[] GetDataBytes(bool forHash = false); - - public byte[] Sign(X509Certificate2 certificate) - { - if (!certificate.HasPrivateKey) - { - throw new InvalidOperationException("You don't have the private key!"); - } - - using (var rsa = certificate.GetRSAPrivateKey()) - { - return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - } - } + [LicenseIgnore] + [JsonIgnore] + public abstract bool ValidLicenseVersion { get; } } diff --git a/src/Core/Models/Business/ILicense.cs b/src/Core/Models/Business/ILicense.cs index 32afc92392..8be52bd87f 100644 --- a/src/Core/Models/Business/ILicense.cs +++ b/src/Core/Models/Business/ILicense.cs @@ -1,5 +1,4 @@ -using System.Security.Cryptography.X509Certificates; -using Bit.Core.Enums; +using Bit.Core.Enums; namespace Bit.Core.Models.Business; @@ -15,6 +14,5 @@ public interface ILicense string Hash { get; set; } string Signature { get; set; } string Token { get; set; } - byte[] GetDataBytes(bool forHash = false); - byte[] Sign(X509Certificate2 certificate); + bool ValidLicenseVersion { get; } } diff --git a/src/Core/Models/Business/OrganizationLicense.cs b/src/Core/Models/Business/OrganizationLicense.cs index 3a7424058f..e8d7f1ae40 100644 --- a/src/Core/Models/Business/OrganizationLicense.cs +++ b/src/Core/Models/Business/OrganizationLicense.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using System.Text; +using System.Text.Json.Serialization; using Bit.Core.AdminConsole.Entities; using Bit.Core.Billing.Enums; using Bit.Core.Billing.Licenses.Attributes; @@ -207,20 +208,14 @@ public class OrganizationLicense : BaseLicense [LicenseIgnore] public bool UseRiskInsights { get; set; } - private bool ValidLicenseVersion + [LicenseIgnore] + [JsonIgnore] + public override bool ValidLicenseVersion { get => Version is >= 1 and <= CurrentLicenseFileVersion + 1; } - public override byte[] GetDataBytes(bool forHash = false) - { - if (!ValidLicenseVersion) - { - throw new NotSupportedException($"Version {Version} is not supported."); - } - return this.GetDataBytesWithAttributes(forHash); - } public bool CanUse( IGlobalSettings globalSettings, diff --git a/src/Core/Models/Business/UserLicense.cs b/src/Core/Models/Business/UserLicense.cs index 1621767562..36e5bccfb8 100644 --- a/src/Core/Models/Business/UserLicense.cs +++ b/src/Core/Models/Business/UserLicense.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using System.Text; +using System.Text.Json.Serialization; using Bit.Core.Billing.Licenses.Attributes; using Bit.Core.Billing.Licenses.Extensions; using Bit.Core.Entities; @@ -60,21 +61,13 @@ public class UserLicense : BaseLicense [LicenseVersion(1)] public short? MaxStorageGb { get; set; } - private bool ValidLicenseVersion + [LicenseIgnore] + [JsonIgnore] + public override bool ValidLicenseVersion { get => Version == 1; } - public override byte[] GetDataBytes(bool forHash = false) - { - if (!ValidLicenseVersion) - { - throw new NotSupportedException($"Version {Version} is not supported."); - } - - return this.GetDataBytesWithAttributes(forHash); - } - public bool CanUse(User user, ClaimsPrincipal claimsPrincipal, out string exception) { var errorMessages = new StringBuilder();