Files
rockfish/util/RavenKeyFactory.cs
2022-10-25 19:54:35 +00:00

452 lines
16 KiB
C#

using System;
using System.Text;
using System.Collections.Generic;
using System.IO;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
namespace rockfishCore.Util
{
//Key generator controller
public static class RavenKeyFactory
{
//Sept 2022 decided all trial periods are 7 days, they can keep renewing a new trial by erasing data or request a longer period case by case
public const int TRIAL_PERIOD_DAYS = 7;
//Unlicensed token
private const string UNLICENSED_TOKEN = "UNLICENSED";
//REVOKED token
public const string REVOKED_TOKEN = "REVOKED";
//LICENSE USER COUNT FEATURES
//SUBSCRIPTION
private const string ACTIVE_INTERNAL_USERS_FEATURE_NAME = "ActiveInternalUsers";
private const string ACTIVE_CUSTOMER_USERS_FEATURE_NAME = "ActiveCustomerUsers";
private const string MAXIMUM_DATA_GB_FEATURE_NAME = "MaximumDataGB";
//Add-on's / integrations
//This feature name means it's a trial key
private const string TRIAL_FEATURE_NAME = "TrialMode";
#region license classes
//DTO object returned on license query
public class LicenseFeature
{
//name of feature / product
public string Feature { get; set; }
//Optional count for items that require it
public long Count { get; set; }
}
//DTO object for parsed key
internal class AyaNovaLicenseKey
{
public AyaNovaLicenseKey()
{
Features = new List<LicenseFeature>();
RegisteredTo = UNLICENSED_TOKEN;
//Id = RegisteredTo;
LicenseFormat = "8";
var vv = Math.Truncate((DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
string sId = vv.ToString();
if (sId.Contains(","))
sId = sId.Split('.')[0];
Id = sId;
}
public override string ToString()
{
System.Text.StringBuilder sb = new StringBuilder();
sb.AppendLine($"Registered to: {RegisteredTo}");
sb.AppendLine($"Database id: {DbId}");
sb.AppendLine($"Type: {(Perpetual ? "Perpetual" : "Subscription")}");
if (WillExpire)
sb.AppendLine($"Available for use until: {LicenseExpiration.ToLongDateString()}");
if (Perpetual)
sb.AppendLine($"Support and updates available until: {MaintenanceExpiration.ToLongDateString()}");
foreach (LicenseFeature f in Features)
{
if (f.Feature == TRIAL_FEATURE_NAME)
{
sb.AppendLine("Temporary license for evaluation");
continue;
}
//default for items added later not tokenized
if (f.Count > 0)
sb.AppendLine($"{f.Feature}: {f.Count}");
else
sb.AppendLine($"{f.Feature}");
}
return sb.ToString();
}
public bool IsEmpty
{
get
{
//Key is empty if it's not registered to anyone or there are no features in it
return string.IsNullOrWhiteSpace(RegisteredTo) || (Features == null || Features.Count == 0);
}
}
/// <summary>
/// Fetch the license status of the feature in question
/// </summary>
/// <param name="Feature"></param>
/// <returns>LicenseFeature object or null if there is no license</returns>
public LicenseFeature GetLicenseFeature(string Feature)
{
if (IsEmpty)
return null;
string lFeature = Feature.ToLowerInvariant();
foreach (LicenseFeature l in Features)
{
if (l.Feature.ToLowerInvariant() == lFeature)
{
return l;
}
}
return null;
}
/// <summary>
/// Check for the existance of license feature
/// </summary>
/// <param name="Feature"></param>
/// <returns>bool</returns>
public bool HasLicenseFeature(string Feature)
{
if (IsEmpty)
return false;
string lFeature = Feature.ToLowerInvariant();
foreach (LicenseFeature l in Features)
{
if (l.Feature.ToLowerInvariant() == lFeature)
{
return true;
}
}
return false;
}
public bool WillExpire
{
get
{
return LicenseExpiration < DateUtil.EmptyDateValue;
}
}
public bool LicenseExpired
{
get
{
return LicenseExpiration > DateTime.Now;
}
}
public bool MaintenanceExpired
{
get
{
return MaintenanceExpiration > DateTime.Now;
}
}
public bool TrialLicense
{
get
{
return HasLicenseFeature(TRIAL_FEATURE_NAME);
}
}
public string LicenseFormat { get; set; }
public string Id { get; set; }
public string RegisteredTo { get; set; }
public string DbId { get; set; }
public bool Perpetual { get; set; }
public DateTime LicenseExpiration { get; set; }
public DateTime MaintenanceExpiration { get; set; }
public List<LicenseFeature> Features { get; set; }
}
#endregion
public static string GetRavenTrialKey(string dbid, string CompanyName, bool Perpetual)
{
//Build a sample test key, sign it and return it
AyaNovaLicenseKey k = new AyaNovaLicenseKey();
k.LicenseFormat = "8";
k.RegisteredTo = CompanyName;
k.DbId = dbid;
k.Perpetual = Perpetual;
//flag as trial key not regular key
k.Features.Add(new LicenseFeature() { Feature = TRIAL_FEATURE_NAME, Count = 0 });
if (Perpetual)
{
//trial period time limit
k.MaintenanceExpiration = k.LicenseExpiration = DateTime.UtcNow.AddDays(TRIAL_PERIOD_DAYS);
//5k inside staff users will cover huge seeding level easily
k.Features.Add(new LicenseFeature() { Feature = ACTIVE_INTERNAL_USERS_FEATURE_NAME, Count = 5000 });
}
else
{
//SUBSCRIPTION
//trial period time limit
k.MaintenanceExpiration = k.LicenseExpiration = DateTime.UtcNow.AddDays(TRIAL_PERIOD_DAYS);
//20k customer contacts will cover huge seeding level easily
//5k inside staff users will cover huge seeding level easily
k.Features.Add(new LicenseFeature() { Feature = ACTIVE_INTERNAL_USERS_FEATURE_NAME, Count = 5000 });
k.Features.Add(new LicenseFeature() { Feature = ACTIVE_CUSTOMER_USERS_FEATURE_NAME, Count = 20000 });
k.Features.Add(new LicenseFeature() { Feature = MAXIMUM_DATA_GB_FEATURE_NAME, Count = 20 });
}
return GenerateRavenKey(k);
}
// //TESTING ONLY this is for development purposes only
// //No external usage
// public static string GetRavenTestKey(string dbid, bool Perpetual)
// {
// //Build a sample test key, sign it and return it
// AyaNovaLicenseKey k = new AyaNovaLicenseKey();
// k.LicenseFormat = "8";
// k.RegisteredTo = "GZ TestCo Inc.";
// k.DbId = dbid;
// k.Perpetual = Perpetual;
// if (Perpetual)
// {
// k.MaintenanceExpiration = k.LicenseExpiration = DateTime.UtcNow.AddDays(TRIAL_PERIOD_DAYS);
// //5k inside staff users will cover huge seeding level easily
// k.Features.Add(new LicenseFeature() { Feature = ACTIVE_INTERNAL_USERS_FEATURE_NAME, Count = 5000 });
// }
// else
// {
// //SUBSCRIPTION
// k.MaintenanceExpiration = k.LicenseExpiration = DateTime.UtcNow.AddDays(TRIAL_PERIOD_DAYS);
// //20k customer contacts will cover huge seeding level easily
// //5k inside staff users will cover huge seeding level easily
// k.Features.Add(new LicenseFeature() { Feature = ACTIVE_INTERNAL_USERS_FEATURE_NAME, Count = 5000 });
// k.Features.Add(new LicenseFeature() { Feature = ACTIVE_CUSTOMER_USERS_FEATURE_NAME, Count = 20000 });
// k.Features.Add(new LicenseFeature() { Feature = MAXIMUM_DATA_GB_FEATURE_NAME, Count = 20 });
// }
// k.Features.Add(new LicenseFeature() { Feature = TRIAL_FEATURE_NAME, Count = 0 });
// return GenerateRavenKey(k);
// }
/// <summary>
/// RAVEN key generator
/// </summary>
/// <returns></returns>
internal static string GenerateRavenKey(AyaNovaLicenseKey k)
{
if (string.IsNullOrWhiteSpace(k.RegisteredTo))
throw new ArgumentException("RegisteredTo is required", "RegisteredTo");
// if (k.DbId == Guid.Empty)
if (string.IsNullOrWhiteSpace(k.DbId))
throw new ArgumentException("DBId is required", "RegisteredTo");
try
{
StringBuilder sbKey = new StringBuilder();
StringWriter sw = new StringWriter(sbKey);
using (Newtonsoft.Json.JsonWriter w = new Newtonsoft.Json.JsonTextWriter(sw))
{
w.Formatting = Newtonsoft.Json.Formatting.Indented;
//outer object start
w.WriteStartObject();
w.WritePropertyName("Key");
w.WriteStartObject();//start of key object
w.WritePropertyName("LicenseFormat");
w.WriteValue(k.LicenseFormat);
w.WritePropertyName("Id");
w.WriteValue(k.Id);
w.WritePropertyName("RegisteredTo");
w.WriteValue(k.RegisteredTo);
w.WritePropertyName("DBID");
w.WriteValue(k.DbId);
w.WritePropertyName("Perpetual");
w.WriteValue(k.Perpetual);
w.WritePropertyName("LicenseExpiration");
w.WriteValue(k.LicenseExpiration);
w.WritePropertyName("MaintenanceExpiration");
w.WriteValue(k.MaintenanceExpiration);
//FEATURES
// w.WritePropertyName("Features");
// w.WriteStartObject();
w.WritePropertyName("Features");
w.WriteStartArray();
foreach (LicenseFeature lf in k.Features)
{
w.WriteStartObject();
w.WritePropertyName("Name");
w.WriteValue(lf.Feature);
if (lf.Count > 0)
{
w.WritePropertyName("Count");
w.WriteValue(lf.Count);
}
w.WriteEndObject();
}
//end of features array
w.WriteEnd();
//end of features object
// w.WriteEndObject();
//end of AyaNova/AyaNovaLite key object
w.WriteEndObject();
//close outer 'wrapper' object brace }
w.WriteEndObject();
}//end of using statement
// ## CALCULATE SIGNATURE
//GET JSON as a string with whitespace stripped outside of delimited strings
//http://stackoverflow.com/questions/8913138/minify-indented-json-string-in-net
string keyNoWS = System.Text.RegularExpressions.Regex.Replace(sbKey.ToString(), "(\"(?:[^\"\\\\]|\\\\.)*\")|\\s+", "$1");
//**** Note this is our real 2016 private key
var privatePEM = @"-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAz7wrvLDcKVMZ31HFGBnLWL08IodYIV5VJkKy1Z0n2snprhSi
u3izxTyz+SLpftvKHJpky027ii7l/pL9Bo3JcjU5rKrxXavnE7TuYPjXn16dNLd0
K/ERSU+pXLmUaVN0nUWuGuUMoGJMEXoulS6pJiG11yu3BM9fL2Nbj0C6a+UwzEHF
mns3J/daZOb4gAzMUdJfh9OJ0+wRGzR8ZxyC99Na2gDmqYglUkSMjwLTL/HbgwF4
OwmoQYJBcET0Wa6Gfb17SaF8XRBV5ZtpCsbStkthGeoXZkFriB9c1eFQLKpBYQo2
DW3H1MPG2nAlQZLbkJj5cSh7/t1bRF08m6P+EQIDAQABAoIBAQCGvTpxLRXgB/Kk
EtmQBEsMx9EVZEwZeKIqKuDsBP8wvf4/10ql5mhT6kehtK9WhSDW5J2z8DtQKZMs
SBKuCZE77qH2CPp9E17SPWzQoRbaW/gDlWpYhgf8URs89XH5zxO4XtXKw/4omRlV
zLYiNR2pifv0EHqpOAg5KGzewdEo4VgXgtRWpHZLMpH2Q0/5ZIKMhstI6vFHP1p7
jmU4YI6uxiu7rVrZDmIUsAGoTdMabNqK/N8hKaoBiIto0Jn1ck26g+emLg8m160y
Xciu5yFUU+PP1SJMUs+k1UnAWf4p46X9jRLQCBRue9o0Ntiq/75aljRoDvgdwDsR
mg4ZANqxAoGBAPBoM5KoMZ4sv8ZFv8V+V8hgL5xiLgGoiwQl91mRsHRM/NQU5A/w
tH8nmwUrJOrksV7kX9228smKmoliTptyGGyi1NPmSkA7cN9YYnENoOEBHCVNK9vh
P+bkbMYUDNMW4fgOj09oXtQtMl5E2B3OTGoNwZ2w13YQJ8RIniLPsX7nAoGBAN01
eQNcUzQk9YrFGTznOs8udDLBfigDxaNnawvPueulJdBy6ZXDDrKmkQQA7xxl8YPr
dNtBq2lOgnb6+smC15TaAfV/fb8BLmkSwdn4Fy0FApIXIEOnLq+wjkte98nuezl8
9KXDzaqNI9hPuk2i36tJuLLMH8hzldveWbWjSlRHAoGBAKRPE7CQtBjfjNL+qOta
RrT0yJWhpMANabYUHNJi+K8ET2jEPnuGkFa3wwPtUPYaCABLJhprB9Unnid3wTIM
8RSO1ddd9jGgbqy3w9Bw+BvQnmQAMpG9iedNB+r5mSpM4XSgvuIO+4EYwuwbMXpt
nVx+um4Eh75xnDxTRYGVYkLRAoGAaZVpUlpR+HSfooHbPv+bSWKB4ewLPCw4vHrT
VErtEfW8q9b9eRcmP81TMFcFykc6VN4g47pfh58KlKHM7DwAjDLWdohIy89TiKGE
V3acEUfv5y0UoFX+6ara8Ey+9upWdKUY3Lotw3ckoc3EPeQ84DQK7YSSswnAgLaL
mS/8fWcCgYBjRefVbEep161d2DGruk4X7eNI9TFJ278h6ydW5kK9aTJuxkrtKIp4
CYf6emoB4mLXFPvAmnsalkhN2iB29hUZCXXSUjpKZrpijL54Wdu2S6ynm7aT97NF
oArP0E2Vbow3JMxq/oeXmHbrLMLQfYyXwFmciLFigOtkd45bfHdrbA==
-----END RSA PRIVATE KEY-----";
PemReader pr = new PemReader(new StringReader(privatePEM));
AsymmetricCipherKeyPair keys = (AsymmetricCipherKeyPair)pr.ReadObject();
var encoder = new UTF8Encoding(false, true);
var inputData = encoder.GetBytes(keyNoWS);
var signer = SignerUtilities.GetSigner("SHA256WITHRSA");
signer.Init(true, keys.Private);
signer.BlockUpdate(inputData, 0, inputData.Length);
var sign = signer.GenerateSignature();
var signature = Convert.ToBase64String(sign);
System.Text.StringBuilder sbOut = new StringBuilder();
sbOut.AppendLine("[KEY");
sbOut.AppendLine(sbKey.ToString());
sbOut.AppendLine("KEY]");
sbOut.AppendLine("[SIGNATURE");
sbOut.AppendLine(signature);
sbOut.AppendLine("SIGNATURE]");
return sbOut.ToString();
}
catch (Exception ex)
{
return (ex.Message);
}
}
//eoc
}
//eons
}