Library / SDK
junian/Standard.Licensing avatar
junian/Standard.Licensing

Standard.Licensing: signed licence files for .NET products

Easy-to-use licensing library for .NET Framework, Mono, .NET Core, and MAUI / Xamarin products

682 stars143 forksC#MIT

At a glance

What is it?
Standard.Licensing is a small MIT licensed C# library that issues licence files signed with ECDSA and validates them by returning a list of failures instead of throwing. It gives you a fluent builder, per-product key pairs and feature flags inside the licence, and it inherits its design from the older Portable.Licensing project.
Who is it for?
Standard.Licensing fits a .NET desktop or MAUI product that sells time-limited or feature-limited licences and wants the licence to be a signed file the customer holds, with a public key shipped in the app. It does not fit a product that needs per-user online activation, a subscription service, or a licence tied to hardware, because nothing in the library talks to a server or binds a licence to a machine.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A licence file signed with ECDSA, not a boolean in the registry

Standard.Licensing exists to solve one problem: proving that a licence file was issued by you and has not been edited since. The README states the mechanism plainly, that the library uses the Elliptic Curve Digital Signature Algorithm to ensure the licence cannot be altered after creation. That choice is the whole design. A licence file is a piece of XML that travels with the customer's install, and without a signature the customer can open it in a text editor and change the expiry date. With a signature, editing the file invalidates it, and the private key that signs never leaves your build machine. The audience is developers shipping .NET products, and the package description covers .NET Framework, Mono, .NET Core and MAUI or Xamarin products, with the repository topics listing dotnet, netstandard, mono, netframework, netcore and xamarin.

One key pair per product, or per release if you resell

Key management is where this library asks you to make a decision rather than hand you a default. The README says you create a new public and private key pair for your product, store the private key securely and distribute the public key with the product. It then draws the line that matters: normally you create one key pair for each product, otherwise it is possible to use a licence with all products using the same key pair. That is a real limitation of the design rather than a documentation gap, because the signature proves the issuer but says nothing about which product it covers unless the key itself is product specific. If you want the customer to buy a new licence on each major release, the README's advice is to create a key pair for each release and each product. The cost of that approach is key distribution: every release now needs its own public key embedded in the application, and old licence files stop validating against a new build unless you keep accepting the old key.

The fluent builder that produces a signed licence file

Licences are created with a builder chain on License.New(), ending in CreateAndSignWithPrivateKey, which both produces the licence and signs it in one step. The chain carries a unique identifier, a licence type, an expiry date, a maximum utilisation count, an optional dictionary of product features, and a licensed-to name and email. Product features are where the commercial terms live, as key and value pairs such as a module name mapped to yes, and a maximum transaction count mapped to a number. The result is then written to disk as text with UTF8 encoding, or handed to an XML writer if your application prefers that. The signature is applied with the encrypted private key string and the pass phrase, so the private key never sits in memory as a plain string during signing.

Validation returns a list of failures, it does not throw

The behaviour of the validation API is the second design decision worth arguing about. The README states that Standard.Licensing will not throw any Exception and just return an enumeration of validation failures, and the validation chain mirrors the builder style, with a condition on the licence type, an And, a signature check against the public key, and a terminal assertion. The result is a lazy enumerable, which is why the README warns you to call ToList() or ToArray() before using the result multiple times, since a lazy sequence that is enumerated twice may re-run the checks or behave differently depending on how it was built. Failure objects expose a type name, a message and a HowToResolve string, so the intended usage is to print them or surface them to a user. The practical upshot is that a missing licence, a wrong signature and an expired trial are all reported the same way, as entries in a collection, rather than as distinct exception types you would catch.

Adding the package and checking the licence at startup

The library is distributed on NuGet as Standard.Licensing, and the installation is a single package reference.

shell
dotnet add package Standard.Licensing

Generating the key pair is the first piece of application code, and the private key is encrypted with a pass phrase as it is created.

csharp
var keyGenerator = Standard.Licensing.Security.Cryptography.KeyGenerator.Create(); 
var keyPair = keyGenerator.GenerateKeyPair(); 
var privateKey = keyPair.ToEncryptedPrivateKeyString(passPhrase);  
var publicKey = keyPair.ToPublicKeyString();

Signing a licence at build time is a matter of chaining the builder.

csharp
var license = License.New()  
    .WithUniqueIdentifier(Guid.NewGuid())  
    .As(LicenseType.Trial)  
    .ExpiresAt(DateTime.Now.AddDays(30))  
    .WithMaximumUtilization(5)  
    .WithProductFeatures(new Dictionary<string, string>  
        {  
            {"Sales Module", "yes"},  
            {"Purchase Module", "yes"},  
            {"Maximum Transactions", "10000"}  
        })  
    .LicensedTo("John Doe", "[email protected]")  
    .CreateAndSignWithPrivateKey(privateKey, passPhrase);

The licence is then saved as a file with UTF8 encoding, or streamed to an XML writer.

csharp
File.WriteAllText("License.lic", license.ToString(), Encoding.UTF8);

At runtime the application loads the licence, validates it and iterates over the failures.

csharp
var license = License.Load(...);
csharp
using Standard.Licensing.Validation;

var validationFailures = license.Validate()  
                                .ExpirationDate(systemDateTime: DateTime.Now)  
                                .When(lic => lic.Type == LicenseType.Trial)  
                                .And()  
                                .Signature(publicKey)  
                                .AssertValidLicense();
csharp
foreach (var failure in validationFailures)
     Console.WriteLine(failure.GetType().Name + ": " + failure.Message + " - " + failure.HowToResolve);

The README says the easiest place to assert the licence is the entry point of your application, and if you only need a yes or no answer, checking whether the collection has any entries is enough.

A fork of Portable.Licensing that widened the platform list

Standard.Licensing is not an original design. The README says the project is derived from the Portable.Licensing library and credits dnauck for the original work, and it gives the reason for the fork in one sentence: the purpose is to add support for more .NET platforms, especially .NET Standard and .NET Core. That history matters for expectations. Portable.Licensing was written when .NET Framework was the target and MAUI did not exist, so the surface area here is an API that has been carried forward rather than redesigned. The repository carries a .gitmodules file, which points to the upstream as a submodule, plus ACKNOWLEDGEMENTS.md, CHANGELOG.md and a ReleaseNotes.txt at the root alongside src and docs directories. There are no GitHub releases published for the project, so ReleaseNotes.txt and the changelog are where version history lives. The licence is MIT, and the homepage for the project is a dedicated site rather than the repository itself.

Where a signed file is the wrong licensing model

Three cases sit outside what this library does. The first is online activation. Nothing here contacts a server, so a licence is granted and revoked entirely by the file the customer holds, and if you need seats that expire on a schedule you need a different mechanism. The second is hardware binding. There is no machine fingerprint in the signature, and the maximum utilisation count is a number inside the licence that the application is free to interpret as it likes, so a determined customer can copy the file to as many machines as they like unless your own code stops them. The third is a product where the licence is not the point. For an open source tool with a paid support contract, a signed licence adds a key management burden without adding protection, and a plain settings flag does the same job. The library also stops at validation. It does not decide what a feature flag unlocks, and there is no built-in mechanism for that; the dictionary you put in the licence is just data your own code has to read and enforce.

Editorial conclusion

Standard.Licensing fits a .NET desktop or MAUI product that sells time-limited or feature-limited licences and wants the licence to be a signed file the customer holds, with a public key shipped in the app. It does not fit a product that needs per-user online activation, a subscription service, or a licence tied to hardware, because nothing in the library talks to a server or binds a licence to a machine. Before you adopt it, decide how many key pairs you will need, since the README warns that one pair per product means a licence signed for one product can be replayed against every other product using that same pair, and convert the result of Validate() with ToList() or ToArray() before you use it twice.

Frequently asked questions

What is Standard.Licensing used for?

It is a .NET library for issuing and checking licence files for software products. The licence is signed with ECDSA so it cannot be altered after creation, and the public key is distributed with the application while the private key stays with the publisher.

How do I install Standard.Licensing?

The package is on NuGet and the README gives the dotnet add package Standard.Licensing command. The project description says it targets .NET Framework, Mono, .NET Core and MAUI or Xamarin products, and the repository topics include netstandard, netframework, netcore, mono and xamarin.

Does Standard.Licensing throw exceptions when a licence is invalid?

No. The README states that the library will not throw any Exception and instead returns an enumeration of validation failures. Each failure carries a type name, a message and a HowToResolve string, and you should call ToList() or ToArray() before using the result more than once.

Should I use one key pair or one per product and release?

The README says you normally create one key pair for each product, because otherwise it is possible to use a licence with all products using the same key pair. If you want customers to buy a new licence on each major release, create a key pair for each release and product, and keep the private key stored securely.

What is the relationship between Standard.Licensing and Portable.Licensing?

Standard.Licensing is a fork of the Portable.Licensing library, credited to dnauck for the original work. The stated purpose of the fork is to add support for more .NET platforms, especially .NET Standard and .NET Core.

Official sources

  1. Issues
  2. junian/Standard.Licensing on GitHub
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/junian-standard-licensing.svg)](https://hysenlabs.com/projects/junian-standard-licensing)

Was this useful?