Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit e81def8

Browse files
committed
Implement License Key Fallback to manually parse License Key with additional Exception details
1 parent edb3ccf commit e81def8

File tree

4 files changed

+142
-16
lines changed

4 files changed

+142
-16
lines changed

src/ServiceStack.Text/LicenseUtils.cs

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
using System.Text.RegularExpressions;
1010
using System.Threading;
1111
using ServiceStack.Text;
12+
using ServiceStack.Text.Common;
1213

1314
namespace ServiceStack
1415
{
1516
public class LicenseException : Exception
1617
{
1718
public LicenseException(string message) : base(message) { }
19+
public LicenseException(string message, Exception innerException) : base(message, innerException) {}
1820
}
1921

2022
public enum LicenseType
@@ -182,17 +184,7 @@ public static void RegisterLicense(string licenseKeyText)
182184
throw new LicenseException("This subscription has been revoked. " + ContactDetails);
183185

184186
var key = PclExport.Instance.VerifyLicenseKeyText(licenseKeyText);
185-
186-
var releaseDate = Env.GetReleaseDate();
187-
if (releaseDate > key.Expiry)
188-
throw new LicenseException("This license has expired on {0} and is not valid for use with this release."
189-
.Fmt(key.Expiry.ToString("d")) + ContactDetails).Trace();
190-
191-
if (key.Type == LicenseType.Trial && DateTime.UtcNow > key.Expiry)
192-
throw new LicenseException("This trial license has expired on {0}."
193-
.Fmt(key.Expiry.ToString("d")) + ContactDetails).Trace();
194-
195-
__activatedLicense = key;
187+
ValidateLicenseKey(key);
196188
}
197189
catch (Exception ex)
198190
{
@@ -201,9 +193,22 @@ public static void RegisterLicense(string licenseKeyText)
201193

202194
var msg = "This license is invalid." + ContactDetails;
203195
if (!string.IsNullOrEmpty(subId))
204-
msg += " The id for this license is '{0}'".Fmt(subId);
196+
msg += $" The id for this license is '{subId}'";
205197

206-
throw new LicenseException(msg).Trace();
198+
lock (typeof(LicenseUtils))
199+
{
200+
try
201+
{
202+
var key = PclExport.Instance.VerifyLicenseKeyTextFallback(licenseKeyText);
203+
ValidateLicenseKey(key);
204+
}
205+
catch (Exception exFallback)
206+
{
207+
throw new LicenseException(msg, exFallback).Trace();
208+
}
209+
}
210+
211+
throw new LicenseException(msg, ex).Trace();
207212
}
208213
finally
209214
{
@@ -213,6 +218,19 @@ public static void RegisterLicense(string licenseKeyText)
213218
}
214219
}
215220

221+
private static void ValidateLicenseKey(LicenseKey key)
222+
{
223+
var releaseDate = Env.GetReleaseDate();
224+
if (releaseDate > key.Expiry)
225+
throw new LicenseException($"This license has expired on {key.Expiry:d} and is not valid for use with this release."
226+
+ ContactDetails).Trace();
227+
228+
if (key.Type == LicenseType.Trial && DateTime.UtcNow > key.Expiry)
229+
throw new LicenseException($"This trial license has expired on {key.Expiry:d}." + ContactDetails).Trace();
230+
231+
__activatedLicense = key;
232+
}
233+
216234
public static void RemoveLicense()
217235
{
218236
__activatedLicense = null;
@@ -365,7 +383,7 @@ public static LicenseKey ToLicenseKey(this string licenseKeyText)
365383
var key = jsv.FromJsv<LicenseKey>();
366384

367385
if (key.Ref != refId)
368-
throw new LicenseException("The license '{0}' is not assigned to CustomerId '{1}'.".Fmt(base64)).Trace();
386+
throw new LicenseException("The license '{0}' is not assigned to CustomerId '{1}'.".Fmt(base64, refId)).Trace();
369387

370388
return key;
371389
}
@@ -376,9 +394,33 @@ public static LicenseKey ToLicenseKey(this string licenseKeyText)
376394
}
377395
}
378396

397+
public static LicenseKey ToLicenseKeyFallback(this string licenseKeyText)
398+
{
399+
licenseKeyText = Regex.Replace(licenseKeyText, @"\s+", "");
400+
var parts = licenseKeyText.SplitOnFirst('-');
401+
var refId = parts[0];
402+
var base64 = parts[1];
403+
var jsv = Convert.FromBase64String(base64).FromUtf8Bytes();
404+
405+
var map = jsv.FromJsv<Dictionary<string, string>>();
406+
var key = new LicenseKey
407+
{
408+
Ref = map.Get("Ref"),
409+
Name = map.Get("Name"),
410+
Type = (LicenseType)Enum.Parse(typeof(LicenseType), map.Get("Type")),
411+
Hash = map.Get("Hash"),
412+
Expiry = DateTimeSerializer.ParseManual(map.Get("Expiry"), DateTimeKind.Utc).GetValueOrDefault(),
413+
};
414+
415+
if (key.Ref != refId)
416+
throw new LicenseException($"The license '{base64}' is not assigned to CustomerId '{refId}'.").Trace();
417+
418+
return key;
419+
}
420+
379421
public static string GetHashKeyToSign(this LicenseKey key)
380422
{
381-
return "{0}:{1}:{2}:{3}".Fmt(key.Ref, key.Name, key.Expiry.ToString("yyyy-MM-dd"), key.Type);
423+
return $"{key.Ref}:{key.Name}:{key.Expiry:yyyy-MM-dd}:{key.Type}";
382424
}
383425

384426
public static Exception GetInnerMostException(this Exception ex)

src/ServiceStack.Text/PclExport.Net40.cs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1133,9 +1133,66 @@ public static bool VerifyLicenseKeyText(this string licenseKeyText, out LicenseK
11331133
return VerifySignedHash(originalData, signedData, publicKeyParams);
11341134
}
11351135

1136+
public static bool VerifyLicenseKeyTextFallback(this string licenseKeyText, out LicenseKey key)
1137+
{
1138+
RSAParameters publicKeyParams;
1139+
try
1140+
{
1141+
var publicRsaProvider = new RSACryptoServiceProvider();
1142+
publicRsaProvider.FromXmlString(LicenseUtils.LicensePublicKey);
1143+
publicKeyParams = publicRsaProvider.ExportParameters(false);
1144+
}
1145+
catch (Exception ex)
1146+
{
1147+
throw new Exception("Could not import LicensePublicKey", ex);
1148+
}
1149+
1150+
try
1151+
{
1152+
key = licenseKeyText.ToLicenseKeyFallback();
1153+
}
1154+
catch (Exception ex)
1155+
{
1156+
throw new Exception("Could not deserialize LicenseKeyText Manually", ex);
1157+
}
1158+
1159+
byte[] originalData;
1160+
byte[] signedData;
1161+
1162+
try
1163+
{
1164+
originalData = key.GetHashKeyToSign().ToUtf8Bytes();
1165+
}
1166+
catch (Exception ex)
1167+
{
1168+
throw new Exception("Could not convert HashKey to UTF-8", ex);
1169+
}
1170+
1171+
try
1172+
{
1173+
signedData = Convert.FromBase64String(key.Hash);
1174+
}
1175+
catch (Exception ex)
1176+
{
1177+
throw new Exception("Could not convert key.Hash from Base64", ex);
1178+
}
1179+
1180+
try
1181+
{
1182+
return VerifySignedHash(originalData, signedData, publicKeyParams);
1183+
}
1184+
catch (Exception ex)
1185+
{
1186+
throw new Exception($"Could not Verify License Key ({originalData.Length}, {signedData.Length})", ex);
1187+
}
1188+
}
1189+
11361190
public static bool VerifySha1Data(this RSACryptoServiceProvider RSAalg, byte[] unsignedData, byte[] encryptedData)
11371191
{
1138-
return RSAalg.VerifyData(unsignedData, new SHA1CryptoServiceProvider(), encryptedData);
1192+
using (var sha = new SHA1CryptoServiceProvider())
1193+
{
1194+
return RSAalg.VerifyData(unsignedData, sha, encryptedData);
1195+
}
11391196
//SL5 || WP
11401197
//return RSAalg.VerifyData(unsignedData, encryptedData, new EMSAPKCS1v1_5_SHA1());
11411198
}

src/ServiceStack.Text/PclExport.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,11 @@ public virtual LicenseKey VerifyLicenseKeyText(string licenseKeyText)
452452
return licenseKeyText.ToLicenseKey();
453453
}
454454

455+
public virtual LicenseKey VerifyLicenseKeyTextFallback(string licenseKeyText)
456+
{
457+
return licenseKeyText.ToLicenseKeyFallback();
458+
}
459+
455460
public virtual void BeginThreadAffinity()
456461
{
457462
}

tests/ServiceStack.Text.Tests/LicensingTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,28 @@ private void AssertKey(string licenseKeyText, LicenseKey expectedKey)
199199
Assert.That(licenseKey.Expiry, Is.EqualTo(expectedKey.Expiry));
200200
}
201201

202+
[Test]
203+
public void Can_deserialize_all_license_key_fallback()
204+
{
205+
AssertKeyFallback(TestBusiness2000Text, TestBusiness2000);
206+
AssertKeyFallback(TestIndie2000Text, TestIndie2000);
207+
AssertKeyFallback(TestBusiness2013Text, TestBusiness2013);
208+
AssertKeyFallback(TestIndie2013Text, TestIndie2013);
209+
AssertKeyFallback(TestTrial2001Text, TestTrial2001);
210+
AssertKeyFallback(TestTrial2016Text, TestTrial2016);
211+
}
212+
213+
private void AssertKeyFallback(string licenseKeyText, LicenseKey expectedKey)
214+
{
215+
var licenseKey = licenseKeyText.ToLicenseKeyFallback();
216+
217+
Assert.That(licenseKey.Ref, Is.EqualTo(expectedKey.Ref));
218+
Assert.That(licenseKey.Name, Is.EqualTo(expectedKey.Name));
219+
Assert.That(licenseKey.Type, Is.EqualTo(expectedKey.Type));
220+
//Assert.That(licenseKey.Hash, Is.EqualTo(expectedKey.Hash));
221+
Assert.That(licenseKey.Expiry, Is.EqualTo(expectedKey.Expiry));
222+
}
223+
202224
#if !NETCORE
203225
[Explicit,Test]
204226
public void Test_dynamically_loaded_assemblies()

0 commit comments

Comments
 (0)