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 rockfishCore.Models; using rockfishCore.Util; namespace rockfishCore.Util { //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(); } /// /// Generate keycode based on passed in data /// This is called by both regular and trial license key routes /// /// private static string genKey(dtoKeyOptions ko, rockfishContext ct) { _plugins = new Dictionary(); if (ko.registeredTo == null || ko.registeredTo == "") throw new ArgumentException("RegisteredTo is required", "RegisteredTo"); if (string.IsNullOrWhiteSpace(ko.emailAddress)) throw new ArgumentException("Email address is required", "emailAddress"); try { StringBuilder sbKey = new StringBuilder(); StringWriter sw = new StringWriter(sbKey); //case 3233 ko.fetchCode = FetchKeyCode.generate(); using (Newtonsoft.Json.JsonWriter w = new Newtonsoft.Json.JsonTextWriter(sw)) { w.Formatting = Newtonsoft.Json.Formatting.Indented; //outer object start w.WriteStartObject(); if (ko.isLite) w.WritePropertyName("AyaNovaLiteLicenseKey"); else 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(ko.registeredTo);//unicode test string //case 3233 w.WritePropertyName("EmailAddress"); w.WriteValue(ko.emailAddress); w.WritePropertyName("FetchCode"); w.WriteValue(ko.fetchCode); //case 3187 - Source here //rockfish w.WritePropertyName("Source"); w.WriteValue(rockfishCore.Util.HexString.ToHex(ko.authorizedUserKeyGeneratorStamp)); w.WritePropertyName("InstallableUntil"); w.WriteValue(ko.installByDate);//case 3542, respect the KO option w.WritePropertyName("TotalScheduleableUsers"); w.WriteValue(ko.users.ToString());//Needs to be a string to match rockfish format w.WritePropertyName("Expires"); w.WriteValue(DateUtil.EpochToDate(ko.supportExpiresDate)); if (ko.keyWillLockout) { w.WritePropertyName("LockDate"); w.WriteValue(DateUtil.EpochToDate(ko.lockoutDate)); } w.WritePropertyName("RequestedTrial"); bool bRequestedTrial = ko.licenseType == "webRequestedTrial"; w.WriteValue(bRequestedTrial.ToString()); //PLUGINS w.WritePropertyName("Plugins"); w.WriteStartObject();//start of key object w.WritePropertyName("Plugin"); w.WriteStartArray(); if (ko.mbi) AddLicensePlugin(w, PLUGIN_MBI_KEY, DateUtil.EpochToDate(ko.mbiSupportExpiresDate)); if (ko.wbi) AddLicensePlugin(w, PLUGIN_WBI_KEY, DateUtil.EpochToDate(ko.wbiSupportExpiresDate)); if (ko.qbi) AddLicensePlugin(w, PLUGIN_QBI_KEY, DateUtil.EpochToDate(ko.qbiSupportExpiresDate)); if (ko.qboi) AddLicensePlugin(w, PLUGIN_QBOI_KEY, DateUtil.EpochToDate(ko.qboiSupportExpiresDate)); if (ko.pti) AddLicensePlugin(w, PLUGIN_PTI_KEY, DateUtil.EpochToDate(ko.ptiSupportExpiresDate)); if (ko.quickNotification) AddLicensePlugin(w, PLUGIN_QUICK_NOTIFICATION_KEY, DateUtil.EpochToDate(ko.quickNotificationSupportExpiresDate)); if (ko.exportToXls) AddLicensePlugin(w, PLUGIN_EXPORT_TO_XLS_KEY, DateUtil.EpochToDate(ko.exportToXlsSupportExpiresDate)); if (ko.outlookSchedule) AddLicensePlugin(w, PLUGIN_OUTLOOK_SCHEDULE_KEY, DateUtil.EpochToDate(ko.outlookScheduleSupportExpiresDate)); if (ko.oli) AddLicensePlugin(w, PLUGIN_OLI_KEY, DateUtil.EpochToDate(ko.oliSupportExpiresDate)); if (ko.importExportCSVDuplicate) AddLicensePlugin(w, PLUGIN_IMPORT_EXPORT_CSV_DUPLICATE_KEY, DateUtil.EpochToDate(ko.importExportCSVDuplicateSupportExpiresDate)); if (ko.ri) AddLicensePlugin(w, PLUGIN_RI_KEY, DateUtil.EpochToDate(ko.riSupportExpiresDate)); //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, 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 }