roger-castaldo_LicenseGenerator/LicenseGenerator/SignedLicense.cs
using Org.Reddragonit.LicenseGenerator.Interfaces;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace Org.Reddragonit.LicenseGenerator
{
    /// <summary>
    /// This class houses a Signed License (a license generated by the Signable License Class).  It can load it from 
    /// the string encoded license as well as the public key, or a license file.  Once loaded,
    /// it can be used to validate aspects of the license (assuming it was valid to being with)
    /// </summary>
    public class SignedLicense
    {
        private readonly License _license;
        private readonly bool _isValid;

        /// <summary>
        /// Create a signed license instance supplying the license string and the public key
        /// </summary>
        /// <param name="licenseString">The encoded license String generated by the SignableLicense</param>
        /// <param name="publicKey">The public key used to sign the license</param>
        /// <param name="isValid">set to tru if the license is valid after loading</param>
        /// <param name="parts">(Optional) Additional License parts that have been written and encoded into the license</param>
        public SignedLicense(string licenseString, string publicKey, out bool isValid, ILicensePart[] parts=null)
        {
            _license = new License(true);
            _license.Load(licenseString, publicKey, out _isValid);
            if (parts != null)
            {
                foreach (ILicensePart ilp in parts)
                    _license.AddAdditionalPart(ilp);
            }
            isValid = _isValid;
        }

        /// <summary>
        /// Create a signed license instance supplying the content of a license file generated by a Signable License
        /// </summary>
        /// <param name="licenseFile">The content of the license file</param>
        /// <param name="isValid">set to tru if the license is valid after loading</param>
        /// <param name="parts">(Optional) Additional License parts that have been written and encoded into the license</param>
        /// <exception cref="FileLoadException">Thrown when the supplied file content is not a valid license file</exception>
        public SignedLicense(byte[] licenseFile, out bool isValid, ILicensePart[] parts = null)
        {
            _license = new License(true);
            string licenseString = null;
            string publicKey = null;
            try
            {
                using MemoryStream ms = new(licenseFile);
                ZipArchive za = new(ms, ZipArchiveMode.Read);
                StreamReader sr;
                foreach (ZipArchiveEntry zae in za.Entries)
                {
                    if (zae.Name == Constants.LicenseFileName)
                    {
                        sr = new StreamReader(zae.Open());
                        licenseString = sr.ReadToEnd();
                        sr.Close();
                    }
                    else if (zae.Name == Constants.KeyFileName)
                    {
                        sr = new StreamReader(zae.Open());
                        publicKey = sr.ReadToEnd();
                        sr.Close();
                    }
                }
            }catch(Exception)
            {
                licenseString = null;
                publicKey = null;
            }
            if (licenseString == null || publicKey == null)
                throw new FileLoadException("The license file specified is not valid and not loadable");
            _license.Load(licenseString, publicKey, out _isValid);
            if (parts != null)
            {
                foreach (ILicensePart ilp in parts)
                    _license.AddAdditionalPart(ilp);
            }
            isValid = _isValid;
        }

        /// <summary>
        /// Called to see if a given application is within the license
        /// </summary>
        /// <param name="applicationID">The unique ID for the application that would have been added by the signable license</param>
        /// <returns>true if the license contains the application id</returns>
        public bool HasApplication(string applicationID)
            =>_isValid && _license.HasApplication(applicationID);

        /// <summary>
        /// Called to see if a license has a given serial number linked in it
        /// </summary>
        /// <param name="serialNumber">The serial number string (must be created from the SerialNumber class)</param>
        /// <returns>true if the license contains the given serial number</returns>
        public bool HasSerialNumber(string serialNumber)
            => _isValid && _license.HasSerialNumber(serialNumber);

        /// <summary>
        /// Houses the Starting Date (if it was set during creation) that the license is valid from.
        /// </summary>
        public DateTime? StartDate
            => _license.StartDate; 

        /// <summary>
        /// Houses the Ending Date (if it was set during creation) that the license is valid until.
        /// </summary>
        public DateTime? EndDate
            => _license.EndDate; 

        /// <summary>
        /// Called to obtain property values that were set as additional properties within this license during its creation.
        /// </summary>
        /// <param name="property">The name of the property in question</param>
        /// <returns>The value of the property, if it exists within the license or a null.</returns>
        public T GetProperty<T>(string property)
        {
            var result = _license[property];
            return result==null ? throw new KeyNotFoundException() : result is JsonElement element ? element.Deserialize<T>() : (T)result;
        }

        /// <summary>
        /// Called to add additional part classes to the license that is already loaded, this is done if they were not supplied in the constructor.
        /// </summary>
        /// <param name="part">The implmentation of a ILicensePart</param>
        public void AddAdditionalPart(ILicensePart part)
            => _license.AddAdditionalPart(part);

        /// <summary>
        /// Called to return all ILicenseParts that exist within the license
        /// </summary>
        public ILicensePart[] AdditionalParts
            => _license.AdditionalParts; 

        /// <summary>
        /// Called to validate the license against the supplied public key, this is used for additional verification when desired
        /// </summary>
        /// <param name="publicKey">The public key that was used to sign the license</param>
        /// <returns>true if its valid</returns>
        public bool Validate(string publicKey)
            => _license.Verify(publicKey);
    }
}
