Skip to content

Instantly share code, notes, and snippets.

@ezekg
Last active January 27, 2022 16:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ezekg/567756b8658f8a2e16a61604484ea608 to your computer and use it in GitHub Desktop.
Save ezekg/567756b8658f8a2e16a61604484ea608 to your computer and use it in GitHub Desktop.
How to verify a signed license key using C#, .NET 6, RSA PKCS1 v1.5 and System.Security.Cryptography. See: https://keygen.sh

Setup dependencies

dotnet --version
# => 6.0.101

Run the example

dotnet run
# => Dataset: {"account":{"id":"1fddcec8-8dd3-4d8d-9b16-215cac0f9b52"},"product":{"id":"7071feff-b5f3-434a-83c1-3ab3f3592325"},"policy":{"id":"9a402512-8e3d-4ac1-92b9-e21317d6723c","duration":null},"user":null,"license":{"id":"a8c85135-ac71-4cfb-afdd-312e3f689037","created":"2021-06-30T11:54:40.159Z","expiry":null}}
#    Valid: True

Older .NET versions

Older versions of .NET may not have the required methods. To work around this, please see this blog post.

using System.Security.Cryptography;
using System.Text;
using System;
class Program
{
const string KEYGEN_LICENSE_KEY = "key/eyJhY2NvdW50Ijp7ImlkIjoiMWZkZGNlYzgtOGRkMy00ZDhkLTliMTYtMjE1Y2FjMGY5YjUyIn0sInByb2R1Y3QiOnsiaWQiOiI3MDcxZmVmZi1iNWYzLTQzNGEtODNjMS0zYWIzZjM1OTIzMjUifSwicG9saWN5Ijp7ImlkIjoiOWE0MDI1MTItOGUzZC00YWMxLTkyYjktZTIxMzE3ZDY3MjNjIiwiZHVyYXRpb24iOm51bGx9LCJ1c2VyIjpudWxsLCJsaWNlbnNlIjp7ImlkIjoiYThjODUxMzUtYWM3MS00Y2ZiLWFmZGQtMzEyZTNmNjg5MDM3IiwiY3JlYXRlZCI6IjIwMjEtMDYtMzBUMTE6NTQ6NDAuMTU5WiIsImV4cGlyeSI6bnVsbH19.egtTIhlFr9PfC8mr47wjzac7XbliK9218JM0APCQSonwfUxYWmN_aVILCbzlmxrrp2LtNmuclHEE_Hz6dihrxua1gYl5WtkhVVpsiE58m1ypsbczDnA1QIj-3uhicQt05mnv2MO7zRL335vcCrse_OFwy8QwZtSswsBYnNa_VE0RVjtrwAf4mrWjB5ASqFc9d-44u2c7R0VVB8ODF0A7telSa9ZbHOkl8gborW45SQT0GLqCK3a1NvHmX_nJ9PR3N8RfHsa3cblmOlsOA-7VCyoJulNdbpwo1xItO28oCOvoqV2P9XDhqfKBEMgyUhgdm_yml3ezJPjfL8l6S-shxw==";
const string KEYGEN_PUBLIC_KEY = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6UEFzZURZdXBLNzhaVWFTYkd3NwpZeVVDQ2VLby8xWHFUQUNPY21UVEhIR2dlSGFjTEsyajlVcmJUbGhXNWg4VnlvMGlVRUhyWTFLZ2Y0d3dpR2dGCmgwWWMrb0RXRGhxMWJJZXJ0STAzQUU0MjBMYnBVZjZPVGlvWCtuWTBFSW54WEYzSjdhQWR4L1IvbllnUkpyTFoKOUFUV2FRVlNnZjN2dHhDdEN3VWVLeEtaSTQxR0EvOUtIVGNDbWQzQnJ5QVExcGlZUHIrcXJFR2YyTkRKZ3IzVwp2VnJNdG5qZW9vcmRBYUNUeVlLdGZtNTZXR1hlWHI0M2RmZGVqQnVJa0k1a3FTendWeW94aG5qRS9SajZ4a3M4CmZmSCtka0FQTndtMElweFhKZXJ5YmptUFd5djdpeVhFVU44Q0tHKzY0MzBEN05vWUhwL2M5OTFaSFFCVXM1OWcKdndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==";
public static void Main (string[] args)
{
using (var rsa = new RSACryptoServiceProvider(2048))
{
// Read signature
var licenseStringTokens = KEYGEN_LICENSE_KEY.Split('.');
var encodedSignature = ConvertBase64UrlString(licenseStringTokens[1]);
var signatureBytes = Convert.FromBase64String(encodedSignature);
// Read data
var signingData = licenseStringTokens[0].Split('/');
var signingDataBytes = Encoding.UTF8.GetBytes($"key/{signingData[1]}");
// Read public key
var publicKey = Encoding.UTF8.GetString(Convert.FromBase64String(KEYGEN_PUBLIC_KEY));
// Import the public key
rsa.ImportFromPem(publicKey);
// Get the OID for SHA256
var shaOID = CryptoConfig.MapNameToOID("SHA256");
// Verify the license key signature
var ok = rsa.VerifyData(signingDataBytes, shaOID, signatureBytes);
if (ok)
{
var encodedDataset = ConvertBase64UrlString(signingData[1]);
var decodedDataset = Convert.FromBase64String(encodedDataset);
var dataset = Encoding.UTF8.GetString(decodedDataset);
Console.WriteLine($"Dataset: {dataset}");
}
Console.WriteLine($"Valid: {ok}");
}
}
// Cryptographic keys use base64url encoding: https://keygen.sh/docs/api/#license-signatures
private static string ConvertBase64UrlString(string s)
{
return s.Replace("-", "+").Replace("_", "/");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment