diff --git a/server/biz/KeyFactory.cs b/server/biz/KeyFactory.cs new file mode 100644 index 0000000..26a7a14 --- /dev/null +++ b/server/biz/KeyFactory.cs @@ -0,0 +1,523 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Sockeye.Models; +using Sockeye.Util; + + + +namespace Sockeye.Biz +{ + //Key generator controller + public static class KeyFactory + { + + public const string PLUGIN_MBI_KEY = "MBI - Minimal browser interface"; + public const string PLUGIN_WBI_KEY = "WBI - Web browser interface"; + public const string PLUGIN_QBI_KEY = "QBI - QuickBooks interface"; + public const string PLUGIN_QBOI_KEY = "QBOI - QuickBooks Online interface"; + public const string PLUGIN_PTI_KEY = "PTI - US Sage 50/Peachtree interface"; + public const string PLUGIN_QUICK_NOTIFICATION_KEY = "QuickNotification"; + public const string PLUGIN_EXPORT_TO_XLS_KEY = "ExportToXls"; + public const string PLUGIN_OUTLOOK_SCHEDULE_KEY = "OutlookSchedule"; + public const string PLUGIN_OLI_KEY = "AyaNovaOLI"; + public const string PLUGIN_IMPORT_EXPORT_CSV_DUPLICATE_KEY = "ImportExportCSVDuplicate"; + public const string PLUGIN_RI_KEY = "RI - Responsive Interface"; + + + private static Dictionary _plugins; + + //Generate a key message reply from a key selection object + //CALLED BY LicenseController Generate route + public static string GetKeyReply(dtoKeyOptions ko, LicenseTemplates t, rockfishContext ct) + { + + //case 3542 + ko.installByDate = System.DateTime.Now.AddYears(1);//changed from one month to one year + string sKey = genKey(ko, ct); + string sMsg = genMessage(sKey, ko, t); + return sMsg; + } + + + + //called by trialkeyrequesthandler.GenerateFromRequest + //get a trial key for named regTo + public static string GetTrialKey(string regTo, bool lite, LicenseTemplates t, string authorizedUserKeyGeneratorStamp, + string emailAddress, rockfishContext ct)//case 3233 + { + DateTime dtoneMonth = System.DateTime.Now.AddMonths(1); + long oneMonth = DateUtil.DateToEpoch(System.DateTime.Now.AddMonths(1)); + + dtoKeyOptions ko = new dtoKeyOptions(); + + //case 3233 + ko.emailAddress = emailAddress; + ko.customerId = 0; //not a customer so trial 0 + + ko.registeredTo = regTo; + ko.supportExpiresDate = oneMonth; + ko.isLite = lite; + ko.installByDate = dtoneMonth; + ko.authorizedUserKeyGeneratorStamp = authorizedUserKeyGeneratorStamp; + if (lite) + { + ko.users = 1; + } + else + { + ko.users = 5; + } + + ko.licenseType = "webRequestedTrial"; + + ko.qbi = true; + ko.qbiSupportExpiresDate = oneMonth; + + ko.qboi = true; + ko.qboiSupportExpiresDate = oneMonth; + + ko.pti = true; + ko.ptiSupportExpiresDate = oneMonth; + + ko.exportToXls = true; + ko.exportToXlsSupportExpiresDate = oneMonth; + + ko.outlookSchedule = true; + ko.outlookScheduleSupportExpiresDate = oneMonth; + + ko.oli = true; + ko.oliSupportExpiresDate = oneMonth; + + ko.importExportCSVDuplicate = true; + ko.importExportCSVDuplicateSupportExpiresDate = oneMonth; + + if (!lite) + { + ko.quickNotification = true; + ko.quickNotificationSupportExpiresDate = oneMonth; + + ko.mbi = true; + ko.mbiSupportExpiresDate = oneMonth; + + ko.wbi = true; + ko.wbiSupportExpiresDate = oneMonth; + + ko.ri = true; + ko.riSupportExpiresDate = oneMonth; + } + + string sKey = genKey(ko, ct); + string sMsg = genMessage(sKey, ko, t); + return sMsg; + + } + + + //Take the key and the options and make a return message ready to send + private static string genMessage(string sKey, dtoKeyOptions ko, LicenseTemplates template) + { + string sMessage = ""; + + if (ko.licenseType == "new") + { + if (ko.isLite) + { + sMessage = template.LiteNew; + } + else + { + sMessage = template.FullNew; + } + } + else if (ko.licenseType == "addon") + { + if (ko.isLite) + { + sMessage = template.LiteAddOn; + } + else + { + sMessage = template.FullAddOn; + } + } + else//licensed trial + { + if (ko.isLite) + { + sMessage = template.LiteTrial; + } + else + { + sMessage = template.FullTrial; + } + } + + //token substitutions + sMessage = sMessage.Replace("[LicenseExpiryDate]", ko.installByDate.ToString("D"));//https://github.com/dotnet/coreclr/issues/2317 + sMessage = sMessage.Replace("[LicenseDescription]", LicenseInfo(ko)); + sMessage = sMessage.Replace("[LicenseKey]", sKey); + + return sMessage; + + } + + + + + // Extra info to display about key at top of key message + private static string LicenseInfo(dtoKeyOptions ko) + { + StringBuilder sb = new StringBuilder(); + sb.Append("LICENSE DETAILS\r\n"); + // sb.Append("This key must be installed before: "); + // sb.Append(ko.installByDate.ToString("D")); + // sb.Append("\r\n"); + + //if (kg.SelectedLicenseType == "Web requested trial") + //{ + // sb.Append("*** This temporary license key has been provided for limited evaluation purposes only *** \r\n"); + // sb.Append("This license will expire and AyaNova usage will be restricted after: " + kg.Expires.ToLongDateString() + "\r\n\r\n"); + //} + + if (ko.keyWillLockout) + { + sb.Append("*** This temporary license key is provided for evaluation use only pending payment ***\r\n"); + sb.Append("This license will expire and AyaNova usage will be restricted after: " + DateUtil.EpochToString(ko.lockoutDate) + "\r\n"); + sb.Append("\r\n"); + sb.Append("A permanent license key will be sent to you when payment \r\n" + + "has been received and processed. There will be no extensions or \r\n" + + "exceptions. Please send in payment early enough to allow for \r\n" + + "mail and processing time to ensure uninterrupted use of AyaNova" + (ko.isLite ? " Lite" : "") + ". \r\n\r\n"); + } + + + + + sb.Append("Registered to: "); + sb.Append(ko.registeredTo); + sb.Append("\r\n"); + + //case 3233 + sb.Append("Fetch address: "); + sb.Append(ko.emailAddress); + sb.Append("\r\n"); + + sb.Append("Fetch code: "); + sb.Append(ko.fetchCode); + sb.Append("\r\n"); + + + sb.Append("Scheduleable resources: "); + switch (ko.users) + { + case 1: + sb.AppendLine("1"); + break; + case 5: + sb.AppendLine("Up to 5"); + break; + case 10: + sb.AppendLine("Up to 10"); + break; + case 15: + sb.AppendLine("Up to 15");//case 3550 + break; + case 20: + sb.AppendLine("Up to 20"); + break; + case 50: + sb.AppendLine("Up to 50"); + break; + case 999: + sb.AppendLine("Up to 999"); + break; + } + + sb.AppendLine("Support and updates until: " + DateUtil.EpochToString(ko.supportExpiresDate) + "\r\n"); + + if (_plugins.Count > 0) + { + sb.Append("\r\n"); + sb.Append("Plugins:\r\n"); + foreach (KeyValuePair kv in _plugins) + { + sb.Append("\t"); + sb.Append(kv.Key); + sb.Append(" support and updates until: "); + sb.Append(kv.Value.ToString("D")); + sb.Append("\r\n"); + } + } + + return sb.ToString(); + } + + + + public static string GenFetchCode() + { + + //sufficient for this purpose + //https://stackoverflow.com/a/1344258/8939 + + var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + var stringChars = new char[10]; + var random = new Random(); + + for (int i = 0; i < stringChars.Length; i++) + { + stringChars[i] = chars[random.Next(chars.Length)]; + } + + var finalString = new String(stringChars); + return finalString; + } + + + + /// + /// Generate keycode based on passed in data + /// This is called by both regular and trial license key routes + /// + /// + private static string genKey(License l) + { + _plugins = new Dictionary(); + + + try + { + + StringBuilder sbKey = new StringBuilder(); + StringWriter sw = new StringWriter(sbKey); + + + l.FetchCode = GenFetchCode(); + + using (Newtonsoft.Json.JsonWriter w = new Newtonsoft.Json.JsonTextWriter(sw)) + { + w.Formatting = Newtonsoft.Json.Formatting.Indented; + + //outer object start + w.WriteStartObject(); + w.WritePropertyName("AyaNovaLicenseKey"); + + w.WriteStartObject();//start of key object + + w.WritePropertyName("SchemaVersion"); + w.WriteValue("7"); + + //stamp a unique value in the key so it can be revoked later + //used to use the digest value of the key for this with xml key + //whole unix timestamp seconds but kept as a double to work beyond 2038 + w.WritePropertyName("Id"); + var vv = Math.Truncate((DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds); + string sId = vv.ToString(); + if (sId.Contains(",")) + sId = sId.Split('.')[0]; + w.WriteValue(sId); + + w.WritePropertyName("Created"); + w.WriteValue(System.DateTime.Now); + + w.WritePropertyName("Sub"); + w.WriteValue("true"); + + w.WritePropertyName("RegisteredTo"); + w.WriteValue(l.RegTo); + + //case 3233 + w.WritePropertyName("EmailAddress"); + w.WriteValue(l.FetchEmail); + + w.WritePropertyName("FetchCode"); + + w.WriteValue(l.FetchCode); + + //case 3187 - Source here + //rockfish + w.WritePropertyName("Source"); + w.WriteValue(StringUtil.ToHex("RFID" + "420")); + + + w.WritePropertyName("InstallableUntil"); + w.WriteValue(l.MaintenanceExpire); + + w.WritePropertyName("TotalScheduleableUsers"); + w.WriteValue(l.Users.ToString());//Needs to be a string to match rockfish format + + w.WritePropertyName("Expires"); + w.WriteValue(l.MaintenanceExpire); + + if (l.LicenseExpire!=null) + { + w.WritePropertyName("LockDate"); + w.WriteValue(l.LicenseExpire); + + } + + w.WritePropertyName("RequestedTrial"); + w.WriteValue(l.TrialMode.ToString()); + + //PLUGINS + w.WritePropertyName("Plugins"); + w.WriteStartObject();//start of key object + w.WritePropertyName("Plugin"); + w.WriteStartArray(); + + if (l.MBI) + AddLicensePlugin(w, PLUGIN_MBI_KEY, l.MBIExpires); + + if (l.WBI) + AddLicensePlugin(w, PLUGIN_WBI_KEY, l.WBIExpires); + + if (l.QBI) + AddLicensePlugin(w, PLUGIN_QBI_KEY, l.QBIExpires); + + if (l.QBOI) + AddLicensePlugin(w, PLUGIN_QBOI_KEY, l.QBOIExpires); + + if (l.PTI) + AddLicensePlugin(w, PLUGIN_PTI_KEY, l.PTIExpires); + + if (l.QuickNotification) + AddLicensePlugin(w, PLUGIN_QUICK_NOTIFICATION_KEY, l.QuickNotificationExpires); + + if (l.ExportToXLS) + AddLicensePlugin(w, PLUGIN_EXPORT_TO_XLS_KEY, l.ExportToXLSExpires); + + if (l.OutlookSchedule) + AddLicensePlugin(w, PLUGIN_OUTLOOK_SCHEDULE_KEY, l.OutlookScheduleExpires); + + if (l.OLI) + AddLicensePlugin(w, PLUGIN_OLI_KEY, l.OLIExpires); + + if (l.ImportExportCSVDuplicate) + AddLicensePlugin(w, PLUGIN_IMPORT_EXPORT_CSV_DUPLICATE_KEY, l.ImportExportCSVDuplicateExpires); + + if (l.RI) + AddLicensePlugin(w, PLUGIN_RI_KEY, l.RIExpires); + + //end of plugins array + w.WriteEnd(); + + //end of plugins 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]"); + + // //case 3233 insert into db + // License l = new License(); + // l.DtCreated = DateUtil.NowAsEpoch(); + // l.Code = ko.fetchCode; + // l.CustomerId = ko.customerId; + // l.Email = ko.emailAddress.ToLowerInvariant(); + // l.Key = sbOut.ToString(); + // l.RegTo = ko.registeredTo; + // ct.License.Add(l); + // ct.SaveChanges(); + + return sbOut.ToString(); + + + } + catch (Exception ex) + { + return (ex.Message); + } + } + + + + private static void AddLicensePlugin(Newtonsoft.Json.JsonWriter w, string pluginName, DateTime? pluginExpires) + { + + //this dictionary is used by the additional message code to + //make the human readable portion of the license + _plugins.Add(pluginName, (DateTime)pluginExpires); + + //this is adding it to the actual key + w.WriteStartObject(); + w.WritePropertyName("Item"); + w.WriteValue(pluginName); + + w.WritePropertyName("SubscriptionExpires"); + w.WriteValue(pluginExpires); + + w.WriteEndObject(); + //---------------- + } + + + + //eoc + } + //eons +} \ No newline at end of file diff --git a/server/biz/LicenseBiz.cs b/server/biz/LicenseBiz.cs index 87c3399..c9f9c6f 100644 --- a/server/biz/LicenseBiz.cs +++ b/server/biz/LicenseBiz.cs @@ -48,7 +48,9 @@ namespace Sockeye.Biz return null; else { - GENERATE KEY AND SET FETCH CODE IF APPLICABLE HERE + await GenerateKey(newObject); + if (HasErrors) return null; + newObject.Tags = TagBiz.NormalizeTags(newObject.Tags); await ct.License.AddAsync(newObject); @@ -453,7 +455,9 @@ namespace Sockeye.Biz putObject.Tags = TagBiz.NormalizeTags(putObject.Tags); await ValidateAsync(putObject, dbObject); if (HasErrors) return null; - GENERATE KEY AND SET FETCH CODE IF APPLICABLE HERE + await GenerateKey(putObject); + if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { @@ -474,6 +478,40 @@ namespace Sockeye.Biz return putObject; } + + + #region KEY GEN + //////////////////////////////////////////////////////////////////////////////////////////////// + //GENERATE + // + internal async Task GenerateKey(License l) + { + + if(l.CustomerId!=null){ + l.CustomerViz=await ct.Customer.AsNoTracking().Where(x => x.Id == l.CustomerId).Select(x => x.Name).FirstOrDefaultAsync(); + } + + switch(l.PGroup){ + case ProductGroup.RavenPerpetual: + { + + } + break; + case ProductGroup.RavenSubscription: + { + + } + break; + case ProductGroup.AyaNova7: + { + + } + break; + } + + } + #endregion key gen + //////////////////////////////////////////////////////////////////////////////////////////////// //DELETE // diff --git a/server/biz/RavenKeyFactory.cs b/server/biz/RavenKeyFactory.cs new file mode 100644 index 0000000..559e714 --- /dev/null +++ b/server/biz/RavenKeyFactory.cs @@ -0,0 +1,460 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Sockeye.Util; + + + + +namespace Sockeye.Biz +{ + //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(); + 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); + } + } + + /// + /// 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 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 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 + + + //Normal code, uncomment this when done testing expiration dates of perpetual + k.MaintenanceExpiration = k.LicenseExpiration = DateTime.UtcNow.AddDays(TRIAL_PERIOD_DAYS); + + //Testing code for 3 minute long evaluation license in case need to test with a key that expires + // #warning FYI ROCKFISH is SET TO GENERATE A VERY SHORT TEST KEY + // k.MaintenanceExpiration = k.LicenseExpiration = DateTime.UtcNow.AddMinutes(3); //TEST VALUE FOR DIAGNOSING LICENSE EXPIRATION ISSUES + + //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);//NOTE: this preserves the current time, should it be set to midnight or something? + + //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); + // } + + + /// + /// 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("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 +} diff --git a/server/util/StringUtil.cs b/server/util/StringUtil.cs index af15598..b30a32b 100644 --- a/server/util/StringUtil.cs +++ b/server/util/StringUtil.cs @@ -1,4 +1,5 @@ using System; +using System.Text; namespace Sockeye.Util { @@ -138,6 +139,32 @@ namespace Sockeye.Util } + public static string ToHex(string str) + { + var sb = new StringBuilder(); + + var bytes = Encoding.ASCII.GetBytes(str); + foreach (var t in bytes) + { + sb.Append(t.ToString("X2")); + } + + return sb.ToString(); // returns: "48656C6C6F20776F726C64" for "Hello world" + } + + + + public static string FromHex(string hexString) + { + var bytes = new byte[hexString.Length / 2]; + for (var i = 0; i < bytes.Length; i++) + { + bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); + } + + return Encoding.ASCII.GetString(bytes); // returns: "Hello world" for "48656C6C6F20776F726C64" + } + }//eoc }//eons \ No newline at end of file