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(License l, bool isFirstLicenseForCustomer) { string sMessage = ""; if (l.TrialMode) { sMessage = TemplateFullTrial; } else { if (isFirstLicenseForCustomer) sMessage = TemplateFullNew; else //add-on or renewal sMessage = TemplateFullAddOn; } //token substitutions sMessage = sMessage.Replace("[LicenseExpiryDate]", l.MaintenanceExpire.ToString("D"));//https://github.com/dotnet/coreclr/issues/2317 sMessage = sMessage.Replace("[LicenseDescription]", LicenseInfo(l)); sMessage = sMessage.Replace("[LicenseKey]", l.Key); return sMessage; } // Extra info to display about key at top of key message public static string LicenseInfo(License l) { 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 (l.LicenseExpire != null) { 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: {l.LicenseExpire}\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. \r\n\r\n"); } sb.Append("Registered to: "); sb.Append(l.RegTo); sb.Append("\r\n"); //case 3233 sb.Append("Fetch address: "); sb.Append(l.FetchEmail); sb.Append("\r\n"); sb.Append("Fetch code: "); sb.Append(l.FetchCode); sb.Append("\r\n"); sb.Append("Scheduleable resources: "); switch (l.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 25://case 4576 sb.AppendLine("Up to 25"); break; case 50: sb.AppendLine("Up to 50"); break; case 999: sb.AppendLine("Up to 999"); break; } sb.AppendLine($"Support and updates until: {l.MaintenanceExpire.ToString("D")}\r\n"); var Plugs = LicenseToPluginsArray(l); if (Plugs.Count > 0) { sb.Append("\r\n"); sb.Append("Plugins:\r\n"); foreach (KeyValuePair kv in Plugs) { 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(); } private static Dictionary LicenseToPluginsArray(License l) { Dictionary Plugs = new Dictionary(); if (l.MBI) Plugs.Add(PLUGIN_MBI_KEY, (DateTime)l.MBIExpires); if (l.WBI) Plugs.Add(PLUGIN_WBI_KEY, (DateTime)l.WBIExpires); if (l.QBI) Plugs.Add(PLUGIN_QBI_KEY, (DateTime)l.QBIExpires); if (l.QBOI) Plugs.Add(PLUGIN_QBOI_KEY, (DateTime)l.QBOIExpires); if (l.PTI) Plugs.Add(PLUGIN_PTI_KEY, (DateTime)l.PTIExpires); if (l.QuickNotification) Plugs.Add(PLUGIN_QUICK_NOTIFICATION_KEY, (DateTime)l.QuickNotificationExpires); if (l.ExportToXLS) Plugs.Add(PLUGIN_EXPORT_TO_XLS_KEY, (DateTime)l.ExportToXLSExpires); if (l.OutlookSchedule) Plugs.Add(PLUGIN_OUTLOOK_SCHEDULE_KEY, (DateTime)l.OutlookScheduleExpires); if (l.OLI) Plugs.Add(PLUGIN_OLI_KEY, (DateTime)l.OLIExpires); if (l.ImportExportCSVDuplicate) Plugs.Add(PLUGIN_IMPORT_EXPORT_CSV_DUPLICATE_KEY, (DateTime)l.ImportExportCSVDuplicateExpires); if (l.RI) Plugs.Add(PLUGIN_RI_KEY, (DateTime)l.RIExpires); return Plugs; } /// /// Generate keycode based on passed in data /// This is called by both regular and trial license key routes /// /// internal static void GenerateAndSetV7Key(License l) { StringBuilder sbKey = new StringBuilder(); StringWriter sw = new StringWriter(sbKey); l.FetchCode = StringUtil.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"); //There is really no need for keys to not be installable now that that they are fetched automatically //this was a holdover from the old manual paste a key in entry system to prevent piracy of using the key repeatedly, now that it's fetched though //this is not necessary so adding 2 years just avoids an issue with the user not fetching right away or a partially expired key w.WriteValue(l.MaintenanceExpire.AddYears(2)); 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]"); l.Key = sbOut.ToString(); } private static void AddLicensePlugin(Newtonsoft.Json.JsonWriter w, string 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(); //---------------- } #region Templates private static string TemplateFullNew { get { return @"Thank you for your purchase and welcome to the world-wide community of AyaNova users! Below is your activation key instructions for entry into the latest trial version of AyaNova 7 that you previously downloaded from our website, installed and tested for suitability. Be sure to review and implement the backup procedures for AyaNova as outlined in the AyaNova online Help document from http://www.ayanova.com/onlinehelp.htm Reminder: no physical product will be shipped to you, this license key fulfills your order in full. PURCHASING ADDITIONAL LICENSES When purchasing additional licenses in future be sure to use the same registration name and registered email address used when you purchased this key. Additional AyaNova licenses can be purchased via the link at http://www.ayanova.com/purchase.htm HELP AND SUPPORT RESOURCES ONLINE: , l. AyaNova web site: http://www.ayanova.com Answers to frequently asked questions: http://www.ayanova.com/faqanswers.htm Technical support forum: http://forum.ayanova.com Latest AyaNova version update information: Downloads and help documentation: http://www.ayanova.com/download.htm http://www.ayanova.com/onlinehelp.htm PURCHASING ADDITIONAL LICENSES When purchasing additional license subscriptions in future do use the same registration name and registered email address used when you purchased your original licenses to ensure fastest processing. [LicenseDescription] HOW TO INSTALL YOUR LICENSE KEY 1. Ensure no other users are running AyaNova 2. Log in as the AyaNova Administrator (default login is manager with password letmein if not yet changed). 3. Select Help->License from the main menu to open the license key form. 4. Select the menu option Fetch license and in the form that opens, copy and paste (preferable) or enter manually your Fetch address and Fetch code exactly as it appears above in 'License Details' section of this message. (If you do not see the 'Fetch license' menu option you need to upgrade to the latest version of AyaNova first) 5.Click on the accept button, AyaNova will fetch the license key from our server and install it automatically"; } } private static string TemplateFullAddOn { get { return @"Thank you for your continued support! This license key when applied, replaces your previous key which is no longer required. [LicenseDescription] HOW TO INSTALL YOUR LICENSE KEY 1. Be sure you are running the latest version before proceeding: https://www.ayanova.com/changeloglatest.htm 2. Ensure no other users are running AyaNova 3. Log in as the AyaNova Administrator (default login is manager with password letmein if not yet changed). 4. Select Help->License from the main menu to open the license key form. 5. Select the menu option Fetch license and in the form that opens, copy and paste (preferable) or enter manually your Fetch address and Fetch code exactly as it appears above in 'License Details' section of this message. (If you do not see the 'Fetch license' menu option you need to upgrade to the latest version of AyaNova first) 6. Click on the accept button, AyaNova will fetch the license key from our server and install it automatically"; } } private static string TemplateFullTrial { get { return @"Thank you for evaluating AyaNova 7 and its optional add-ons. Below are instructions for license entry into the latest trial version of AyaNova that you previously downloaded from our website. You must apply this license before [LicenseExpiryDate]. HELP AND SUPPORT RESOURCES ONLINE: AyaNova web site: http://www.ayanova.com Answers to frequently asked questions: http://www.ayanova.com/faqanswers.htm Technical support forum: http://forum.ayanova.com Latest AyaNova version update information: Downloads and help documentation: http://www.ayanova.com/download.htm http://www.ayanova.com/onlinehelp.htm [LicenseDescription] HOW TO INSTALL YOUR LICENSE KEY 1. Ensure no other users are running AyaNova 2. Log in as the AyaNova Administrator (default login is manager with password letmein if not yet changed). 3. Select Help->License from the main menu to open the license key form. 4. Select the menu option Fetch license and in the form that opens, copy and paste (preferable) or enter manually your Fetch address and Fetch code exactly as it appears above in License Details section. (If you do not see the 'Fetch license' menu option you need to upgrade to the latest version of AyaNova first) 5. Click on the accept button, AyaNova will fetch the license key from our server and install it automatically"; } } #endregion templates //eoc } //eons }