501 lines
18 KiB
C#
501 lines
18 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
|
|
{
|
|
public const int TRIAL_PERIOD_DAYS = 5;
|
|
|
|
//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";
|
|
//Add-on's / integrations
|
|
//thse are now included so not relevant here but keeping for renaming for future subscription add-on's
|
|
// private const string OLI_FEATURE_NAME = "OLI";
|
|
// private const string QBI_FEATURE_NAME = "QBI";
|
|
// private const string QBOI_FEATURE_NAME = "QBOI";
|
|
//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 SUBSCRIPTION_FEATURE_NAME = "Subscription";
|
|
|
|
#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()
|
|
{
|
|
/*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 == SUBSCRIPTION_FEATURE_NAME)
|
|
{
|
|
sb.AppendLine("Subscription license");
|
|
continue;
|
|
}
|
|
// if (f.Feature == QBI_FEATURE_NAME)
|
|
// {
|
|
// sb.AppendLine("QuickBooks desktop integration option");
|
|
// continue;
|
|
// }
|
|
// if (f.Feature == QBOI_FEATURE_NAME)
|
|
// {
|
|
// sb.AppendLine("QuickBooks online integration 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);
|
|
}
|
|
}
|
|
|
|
/// <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 bool RentalLicense
|
|
{
|
|
get
|
|
{
|
|
return HasLicenseFeature(SUBSCRIPTION_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<LicenseFeature> 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"": ""Subscription"" <----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 = SUBSCRIPTION_FEATURE_NAME, Count = 0 });
|
|
|
|
//add every possible feature
|
|
//k.Features.Add(new LicenseFeature() { Feature = QBI_FEATURE_NAME, Count = 0 });
|
|
k.Features.Add(new LicenseFeature() { Feature = SERVICE_TECHS_FEATURE_NAME, Count = 100 });
|
|
|
|
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("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 = QBI_FEATURE_NAME, Count = 0 });
|
|
// k.Features.Add(new LicenseFeature() { Feature = QBOI_FEATURE_NAME, Count = 0 });
|
|
// k.Features.Add(new LicenseFeature() { Feature = OLI_FEATURE_NAME, Count = 0 });
|
|
k.Features.Add(new LicenseFeature() { Feature = SERVICE_TECHS_FEATURE_NAME, Count = 100 });
|
|
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 = SUBSCRIPTION_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 |