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 { public const int TRIAL_PERIOD_DAYS = 45; //Unlicensed token private const string UNLICENSED_TOKEN = "UNLICENSED"; //REVOKED token public const string REVOKED_TOKEN = "REVOKED"; //FEATURE NAMES //Scheduleable users private const string SERVICE_TECHS_FEATURE_NAME = "ServiceTechs"; //Accounting add-on private const string ACCOUNTING_FEATURE_NAME = "Accounting"; //This feature name means it's a trial key private const string TRIAL_FEATURE_NAME = "TrialMode"; //This feature name means it's a SAAS or rental mode key for month to month hosted service private const string RENTAL_FEATURE_NAME = "ServiceMode"; #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(); 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() { /*LICENSE DETAILS Registered to: Montacargas y Equipos Colombia Fetch address: facordoba@jycexports.com Fetch code: ZgVySojpHg Scheduleable resources: Up to 15 Support and updates until: Thursday, August 13, 2020 Plugins: WBI - Web browser interface support and updates until: Tuesday, June 15, 2021 ExportToXls support and updates until: Tuesday, June 15, 2021 ImportExportCSVDuplicate support and updates until: Friday, July 17, 2020 RI - Responsive Interface support and updates until: Tuesday, June 15, 2021 */ System.Text.StringBuilder sb = new StringBuilder(); sb.AppendLine("LICENSE DETAILS"); sb.AppendLine($"Registered to: {RegisteredTo}"); sb.AppendLine($"Database id: {DbId}"); if (WillExpire) sb.AppendLine($"License valid until: {LicenseExpiration.ToLongDateString()}"); sb.AppendLine($"Support and updates until: {MaintenanceExpiration.ToLongDateString()}"); foreach (LicenseFeature f in Features) { if (f.Feature == TRIAL_FEATURE_NAME) { sb.AppendLine("Temporary license for evaluation"); continue; } if (f.Feature == RENTAL_FEATURE_NAME) { sb.AppendLine("Service / Rental license"); continue; } if (f.Feature == ACCOUNTING_FEATURE_NAME) { sb.AppendLine("Accounting option"); continue; } if (f.Feature == SERVICE_TECHS_FEATURE_NAME) { sb.AppendLine($"Scheduleable resources: {f.Count}"); 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); } } /// /// Fetch the license status of the feature in question /// /// /// LicenseFeature object or null if there is no license 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; } /// /// Check for the existance of license feature /// /// /// bool 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 bool RentalLicense { get { return HasLicenseFeature(RENTAL_FEATURE_NAME); } } public string LicenseFormat { get; set; } public string Id { get; set; } public string RegisteredTo { get; set; } public string DbId { get; set; } public DateTime LicenseExpiration { get; set; } public DateTime MaintenanceExpiration { get; set; } public List Features { get; set; } } #endregion #region sample v8 key // private static string SAMPLE_KEY = @"[KEY // { // ""Key"": { // ""LicenseFormat"": ""8"", // ""Id"": ""34-1516288681"", <----Customer id followed by key serial id // ""RegisteredTo"": ""Super TestCo"", // ""DBID"": ""df558559-7f8a-4c7b-955c-959ebcdf71f3"", // ""LicenseExpiration"": ""2019-01-18T07:18:01.2329138-08:00"", <--- UTC,special 1/1/5555 DateTime if perpetual license, applies to all features 1/1/5555 indicates not expiring // ""MaintenanceExpiration"": ""2019-01-18T07:18:01.2329138-08:00"", <-- UTC, DateTime support and updates subscription runs out, applies to all features // ""Features"": { <-- deprecate, collection doesn't need to be inside a property? // ""Feature"": [ // { // ""Name"": ""ServiceTechs"", // ""Count"":""10"", // }, // { // ""Name"": ""Accounting"" // }, // { // ""Name"": ""TrialMode""<---means is a trial key // }, // { // ""Name"": ""ServiceMode"" <----Means it's an SAAS/Rental key // } // ] // } // } // } // KEY] // [SIGNATURE // HEcY3JCVwK9HFXEFnldUEPXP4Q7xoZfMZfOfx1cYmfVF3MVWePyZ9dqVZcY7pk3RmR1BbhQdhpljsYLl+ZLTRhNa54M0EFa/bQnBnbwYZ70EQl8fz8WOczYTEBo7Sm5EyC6gSHtYZu7yRwBvhQzpeMGth5uWnlfPb0dMm0DQM7PaqhdWWW9GCSOdZmFcxkFQ8ERLDZhVMbd8PJKyLvZ+sGMrmYTAIoL0tqa7nrxYkM71uJRTAmQ0gEl4bJdxiV825U1J+buNQuTZdacZKEPSjQQkYou10jRbReUmP2vDpvu+nA1xdJe4b5LlyQL+jiIXH17lf93xlCUb0UkDpu8iNQ== // SIGNATURE]\"; #endregion public static string GetRavenTrialKey(string dbid, string CompanyName) { //Build a sample test key, sign it and return it AyaNovaLicenseKey k = new AyaNovaLicenseKey(); k.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]; // k.Id = $"00-{sId}"; k.RegisteredTo = CompanyName; k.DbId = dbid; //trial period time limit k.MaintenanceExpiration = k.LicenseExpiration = DateTime.UtcNow.AddDays(TRIAL_PERIOD_DAYS); //flag as trial key not regular key k.Features.Add(new LicenseFeature() { Feature = TRIAL_FEATURE_NAME, Count = 0 }); //flag it as a temporary expiring key so that the expiration date takes effect k.Features.Add(new LicenseFeature() { Feature = RENTAL_FEATURE_NAME, Count = 0 }); //add every possible feature k.Features.Add(new LicenseFeature() { Feature = ACCOUNTING_FEATURE_NAME, Count = 0 }); k.Features.Add(new LicenseFeature() { Feature = SERVICE_TECHS_FEATURE_NAME, Count = 1000 }); return GenerateRavenKey(k); } /// /// RAVEN key generator /// /// 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("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); } } public static string GetRavenTestKey(string dbid) { //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.Features.Add(new LicenseFeature() { Feature = ACCOUNTING_FEATURE_NAME, Count = 0 }); k.Features.Add(new LicenseFeature() { Feature = SERVICE_TECHS_FEATURE_NAME, Count = 1000 }); k.MaintenanceExpiration = k.LicenseExpiration = DateTime.UtcNow.AddMonths(1); k.Features.Add(new LicenseFeature() { Feature = TRIAL_FEATURE_NAME, Count = 0 }); k.Features.Add(new LicenseFeature() { Feature = RENTAL_FEATURE_NAME, Count = 0 }); return GenerateRavenKey(k); } //eoc } //eons } // #region RAVEN test code for development // //Trial key magic number for development and testing, all other guids will be fully licensed // private static Guid TEST_TRIAL_KEY_DBID = new Guid("{A6D18A8A-5613-4979-99DA-80D07641A2FE}"); // #endregion