Skip to content

Instantly share code, notes, and snippets.

@ezekg
Created June 30, 2021 11:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ezekg/37ebfa5e5295f9643af9545f5ba2d09e to your computer and use it in GitHub Desktop.
Save ezekg/37ebfa5e5295f9643af9545f5ba2d09e to your computer and use it in GitHub Desktop.
Using BouncyCastle to verify license keys cryptographically signed using RSA PKCS1 v1.5.
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Security;
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)
{
var pemPublicKey = Encoding.UTF8.GetString(
Convert.FromBase64String(KEYGEN_PUBLIC_KEY)
);
// Parse and convert the base64 PEM public key to ASN1 format
var encodedAns1PublicKey = pemPublicKey
.Replace("-----BEGIN PUBLIC KEY-----", string.Empty)
.Replace("-----END PUBLIC KEY-----", string.Empty)
.Trim();
var asn1PublicKey = Convert.FromBase64String(encodedAns1PublicKey);
// Import the public key
var publicKey = PublicKeyFactory.CreateKey(asn1PublicKey);
// Initialize RSA
var digest = new Sha256Digest();
var engine = new RsaEngine();
var rsa = new RsaDigestSigner(digest);
rsa.Init(false, publicKey);
// Parse the license key
var licenseKey = KEYGEN_LICENSE_KEY;
var keyParts = licenseKey.Split('.');
var signingData = keyParts[0];
var encodedSignature = ConvertBase64UrlString(keyParts[1]);
var dataParts = signingData.Split('/');
var signingPrefix = dataParts[0];
var encodedData = ConvertBase64UrlString(dataParts[1]);
if (signingPrefix != "key")
{
Console.WriteLine("[ERROR] Invalid license key prefix: prefix={0}", signingPrefix);
Environment.Exit(1);
}
// Convert data to bytes for verification
var signingDataBytes = Encoding.UTF8.GetBytes($"key/{encodedData}");
var signatureBytes = Convert.FromBase64String(encodedSignature);
// Verify the license key's signature
rsa.BlockUpdate(signingDataBytes, 0, signingDataBytes.Length);
var ok = rsa.VerifySignature(signatureBytes);
if (ok)
{
var decodedDataBytes = Convert.FromBase64String(encodedData);
var decodedData = Encoding.UTF8.GetString(decodedDataBytes);
Console.WriteLine("[INFO] License key is cryptographically valid: key={0} dataset={1}", licenseKey, decodedData);
Environment.Exit(0);
}
else
{
Console.WriteLine("[ERROR] License key is not valid: key={0}", licenseKey);
Environment.Exit(1);
}
}
// 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