diff --git a/src/Standard.Licensing.sln b/src/Standard.Licensing.sln index 106691d..4b212ee 100644 --- a/src/Standard.Licensing.sln +++ b/src/Standard.Licensing.sln @@ -4,7 +4,7 @@ VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Standard.Licensing", "Standard.Licensing\Standard.Licensing.csproj", "{C658755A-2F60-430B-A41E-9C11C817B909}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Standard.Licensing.Tests", "Standard.Licensing.Tests\Standard.Licensing.Tests.csproj", "{3BF56CDC-AA67-4BBC-A7F6-422920E0E2F9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Standard.Licensing.Tests", "Standard.Licensing.Tests\Standard.Licensing.Tests.csproj", "{3BF56CDC-AA67-4BBC-A7F6-422920E0E2F9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Standard.Licensing/License.cs b/src/Standard.Licensing/License.cs index 397e10e..ae8d8c7 100644 --- a/src/Standard.Licensing/License.cs +++ b/src/Standard.Licensing/License.cs @@ -42,6 +42,12 @@ public class License { private readonly XElement xmlData; private readonly string signatureAlgorithm = X9ObjectIdentifiers.ECDsaWithSha512.Id; + private static XmlReaderSettings defaultXmlReaderSettings = new XmlReaderSettings + { + IgnoreWhitespace = true, // Ignore unnecessary whitespace nodes + IgnoreComments = true, // Skip comment nodes to reduce processing + IgnoreProcessingInstructions = true // Skip processing instructions + }; /// /// Initializes a new instance of the class. @@ -279,7 +285,12 @@ public static License Load(string xmlString) /// A populated from the that contains XML. public static License Load(Stream stream) { - return new License(XElement.Load(stream, LoadOptions.None)); + // Use XmlReader for efficient XML parsing + using (var reader = XmlReader.Create(stream, defaultXmlReaderSettings)) + { + var xmlData = XElement.Load(reader); + return new License(xmlData); + } } /// @@ -301,7 +312,9 @@ public static License Load(TextReader reader) /// A populated from the that contains XML. public static License Load(XmlReader reader) { - return new License(XElement.Load(reader, LoadOptions.None)); + // Directly load the XML from the XmlReader + var xmlData = XElement.Load(reader); + return new License(xmlData); } /// @@ -311,7 +324,11 @@ public static License Load(XmlReader reader) /// will be written to. public void Save(Stream stream) { - xmlData.Save(stream); + // Use XmlWriter for efficient XML writing + using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Indent = true })) + { + xmlData.WriteTo(writer); + } } /// @@ -321,7 +338,10 @@ public void Save(Stream stream) /// will be written to. public void Save(TextWriter textWriter) { - xmlData.Save(textWriter); + using (var writer = XmlWriter.Create(textWriter, new XmlWriterSettings { Indent = true })) + { + xmlData.WriteTo(writer); + } } /// diff --git a/src/Standard.Licensing/Security/Cryptography/KeyFactory.cs b/src/Standard.Licensing/Security/Cryptography/KeyFactory.cs index f4f01b7..77d7de8 100644 --- a/src/Standard.Licensing/Security/Cryptography/KeyFactory.cs +++ b/src/Standard.Licensing/Security/Cryptography/KeyFactory.cs @@ -24,10 +24,15 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Security; using Org.BouncyCastle.X509; using System; +using System.Buffers; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +#if NET6_0_OR_GREATER +using System.Security.Cryptography; // Use native cryptography in .NET 6.0+ +#endif namespace Standard.Licensing.Security.Cryptography { @@ -43,14 +48,30 @@ internal static class KeyFactory /// The encrypted private key. public static string ToEncryptedPrivateKeyString(AsymmetricKeyParameter key, string passPhrase) { - var salt = new byte[16]; - var secureRandom = SecureRandom.GetInstance("SHA256PRNG"); - secureRandom.SetSeed(secureRandom.GenerateSeed(16)); //See Bug #135 - secureRandom.NextBytes(salt); + // Rent a buffer for the salt (16 bytes for the salt) + var salt = ArrayPool.Shared.Rent(16); - return - Convert.ToBase64String(PrivateKeyFactory.EncryptKey(keyEncryptionAlgorithm, passPhrase.ToCharArray(), - salt, 10, key)); + try + { +#if NET6_0_OR_GREATER + // Use built-in cryptography (e.g., RNGCryptoServiceProvider) in .NET 6.0+ + RandomNumberGenerator.Fill(salt.AsSpan(0, 16)); +#else + // Use BouncyCastle for .NET Standard 2.0 or other versions + var secureRandom = SecureRandom.GetInstance("SHA256PRNG"); + secureRandom.SetSeed(secureRandom.GenerateSeed(16)); // Seed generation + secureRandom.NextBytes(salt, 0, 16); +#endif + // Encrypt the key (BouncyCastle library used for all versions) + var encryptedKey = PrivateKeyFactory.EncryptKey(keyEncryptionAlgorithm, passPhrase.ToCharArray(), salt, 10, key); + + return Convert.ToBase64String(encryptedKey); + } + finally + { + // Return the rented buffer to the pool + ArrayPool.Shared.Return(salt); + } } /// diff --git a/src/Standard.Licensing/Standard.Licensing.csproj b/src/Standard.Licensing/Standard.Licensing.csproj index 1242543..ddf1a87 100644 --- a/src/Standard.Licensing/Standard.Licensing.csproj +++ b/src/Standard.Licensing/Standard.Licensing.csproj @@ -1,4 +1,4 @@ - + net8.0;net6.0;netstandard2.0;net461 @@ -47,6 +47,7 @@ v1.1.9 +