3
3
4
4
using System ;
5
5
using System . Collections . Generic ;
6
+ using System . ComponentModel ;
6
7
using System . Globalization ;
7
8
using System . IO ;
8
9
using System . Linq ;
@@ -22,6 +23,8 @@ public LicenseException(string message, Exception innerException) : base(message
22
23
public enum LicenseType
23
24
{
24
25
Free ,
26
+ FreeIndividual ,
27
+ FreeOpenSource ,
25
28
Indie ,
26
29
Business ,
27
30
Enterprise ,
@@ -210,6 +213,12 @@ public static void RegisterLicense(string licenseKeyText)
210
213
Thread . CurrentThread . CurrentCulture = CultureInfo . InvariantCulture ;
211
214
try
212
215
{
216
+ if ( IsFreeLicenseKey ( licenseKeyText ) )
217
+ {
218
+ ValidateFreeLicenseKey ( licenseKeyText ) ;
219
+ return ;
220
+ }
221
+
213
222
var parts = licenseKeyText . SplitOnFirst ( '-' ) ;
214
223
subId = parts [ 0 ] ;
215
224
@@ -227,7 +236,7 @@ public static void RegisterLicense(string licenseKeyText)
227
236
catch ( Exception ex )
228
237
{
229
238
//bubble unrelated project Exceptions
230
- if ( ex is FileNotFoundException || ex is FileLoadException || ex is BadImageFormatException )
239
+ if ( ex is FileNotFoundException || ex is FileLoadException || ex is BadImageFormatException || ex is NotSupportedException )
231
240
throw ;
232
241
233
242
if ( ex is LicenseException )
@@ -277,6 +286,162 @@ private static void ValidateLicenseKey(LicenseKey key)
277
286
if ( LicenseWarningMessage != null )
278
287
Console . WriteLine ( LicenseWarningMessage ) ;
279
288
}
289
+
290
+ private const string IndividualPrefix = "Individual (c) " ;
291
+ private const string OpenSourcePrefix = "OSS " ;
292
+
293
+ private static bool IsFreeLicenseKey ( string licenseText ) =>
294
+ licenseText . StartsWith ( IndividualPrefix ) || licenseText . StartsWith ( OpenSourcePrefix ) ;
295
+
296
+ private static void ValidateFreeLicenseKey ( string licenseText )
297
+ {
298
+ if ( ! IsFreeLicenseKey ( licenseText ) )
299
+ throw new NotSupportedException ( "Not a free License Key" ) ;
300
+
301
+ var envKey = Environment . GetEnvironmentVariable ( "SERVICESTACK_LICENSE" ) ;
302
+ if ( envKey == licenseText )
303
+ throw new LicenseException ( "Cannot use SERVICESTACK_LICENSE Environment variable with free License Keys, " +
304
+ "please use Licensing.RegisterLicense() in source code." ) ;
305
+
306
+ LicenseKey key = null ;
307
+ if ( licenseText . StartsWith ( IndividualPrefix ) )
308
+ {
309
+ key = VerifyIndividualLicense ( licenseText ) ;
310
+ if ( key == null )
311
+ throw new LicenseException ( "Individual License Key is invalid." ) ;
312
+ }
313
+ else if ( licenseText . StartsWith ( OpenSourcePrefix ) )
314
+ {
315
+ key = VerifyOpenSourceLicense ( licenseText ) ;
316
+ if ( key == null )
317
+ throw new LicenseException ( "Open Source License Key is invalid." ) ;
318
+ }
319
+ else throw new NotSupportedException ( "Not a free License Key" ) ;
320
+
321
+ var releaseDate = Env . GetReleaseDate ( ) ;
322
+ if ( releaseDate > key . Expiry )
323
+ throw new LicenseException ( $ "This license has expired on { key . Expiry : d} and is not valid for use with this release.\n "
324
+ + "Check https://servicestack.net/free for eligible renewals." ) . Trace ( ) ;
325
+
326
+ __activatedLicense = new __ActivatedLicense ( key ) ;
327
+ }
328
+
329
+ internal static string Info => __activatedLicense ? . LicenseKey == null
330
+ ? "NO"
331
+ : __activatedLicense . LicenseKey . Type switch {
332
+ LicenseType . Free => "FR" ,
333
+ LicenseType . FreeIndividual => "FI" ,
334
+ LicenseType . FreeOpenSource => "FO" ,
335
+ LicenseType . Indie => "IN" ,
336
+ LicenseType . Business => "BU" ,
337
+ LicenseType . Enterprise => "EN" ,
338
+ LicenseType . TextIndie => "TI" ,
339
+ LicenseType . TextBusiness => "TB" ,
340
+ LicenseType . OrmLiteIndie => "OI" ,
341
+ LicenseType . OrmLiteBusiness => "OB" ,
342
+ LicenseType . RedisIndie => "RI" ,
343
+ LicenseType . RedisBusiness => "RB" ,
344
+ LicenseType . AwsIndie => "AI" ,
345
+ LicenseType . AwsBusiness => "AB" ,
346
+ LicenseType . Trial => "TR" ,
347
+ LicenseType . Site => "SI" ,
348
+ LicenseType . TextSite => "TS" ,
349
+ LicenseType . RedisSite => "RS" ,
350
+ LicenseType . OrmLiteSite => "OS" ,
351
+ _ => "UN" ,
352
+ } ;
353
+
354
+ private static LicenseKey VerifyIndividualLicense ( string licenseKey )
355
+ {
356
+ if ( licenseKey == null )
357
+ return null ;
358
+ if ( licenseKey . Length < 100 )
359
+ return null ;
360
+ if ( ! licenseKey . StartsWith ( IndividualPrefix ) )
361
+ return null ;
362
+ var keyText = licenseKey . LastLeftPart ( ' ' ) ;
363
+ var keySign = licenseKey . LastRightPart ( ' ' ) ;
364
+ if ( keySign . Length < 48 )
365
+ return null ;
366
+
367
+ try
368
+ {
369
+ var rsa = System . Security . Cryptography . RSA . Create ( ) ;
370
+ rsa . FromXml ( LicensePublicKey ) ;
371
+
372
+ #if ! NETCORE
373
+ var verified = ( ( System . Security . Cryptography . RSACryptoServiceProvider ) rsa )
374
+ . VerifyData ( keyText . ToUtf8Bytes ( ) , "SHA256" , Convert . FromBase64String ( keySign ) ) ;
375
+ #else
376
+ var verified = rsa . VerifyData ( keyText . ToUtf8Bytes ( ) ,
377
+ Convert . FromBase64String ( keySign ) ,
378
+ System . Security . Cryptography . HashAlgorithmName . SHA256 ,
379
+ System . Security . Cryptography . RSASignaturePadding . Pkcs1 ) ;
380
+ #endif
381
+ if ( verified )
382
+ {
383
+ var yearStr = keyText . Substring ( IndividualPrefix . Length ) . LeftPart ( ' ' ) ;
384
+ if ( yearStr . Length == 4 && int . TryParse ( yearStr , out var year ) )
385
+ {
386
+ return new LicenseKey {
387
+ Expiry = new DateTime ( year + 1 , 1 , 1 , 0 , 0 , 0 , DateTimeKind . Utc ) ,
388
+ Hash = keySign ,
389
+ Name = keyText ,
390
+ Type = LicenseType . FreeIndividual ,
391
+ } ;
392
+ }
393
+ }
394
+ }
395
+ catch { }
396
+
397
+ return null ;
398
+ }
399
+
400
+ private static LicenseKey VerifyOpenSourceLicense ( string licenseKey )
401
+ {
402
+ if ( licenseKey == null )
403
+ return null ;
404
+ if ( licenseKey . Length < 100 )
405
+ return null ;
406
+ if ( ! licenseKey . StartsWith ( OpenSourcePrefix ) )
407
+ return null ;
408
+ var keyText = licenseKey . LastLeftPart ( ' ' ) ;
409
+ var keySign = licenseKey . LastRightPart ( ' ' ) ;
410
+ if ( keySign . Length < 48 )
411
+ return null ;
412
+
413
+ try
414
+ {
415
+ var rsa = System . Security . Cryptography . RSA . Create ( ) ;
416
+ rsa . FromXml ( LicensePublicKey ) ;
417
+
418
+ #if ! NETCORE
419
+ var verified = ( ( System . Security . Cryptography . RSACryptoServiceProvider ) rsa )
420
+ . VerifyData ( keyText . ToUtf8Bytes ( ) , "SHA256" , Convert . FromBase64String ( keySign ) ) ;
421
+ #else
422
+ var verified = rsa . VerifyData ( keyText . ToUtf8Bytes ( ) ,
423
+ Convert . FromBase64String ( keySign ) ,
424
+ System . Security . Cryptography . HashAlgorithmName . SHA256 ,
425
+ System . Security . Cryptography . RSASignaturePadding . Pkcs1 ) ;
426
+ #endif
427
+ if ( verified )
428
+ {
429
+ var yearStr = keyText . Substring ( OpenSourcePrefix . Length ) . RightPart ( ' ' ) . LeftPart ( ' ' ) ;
430
+ if ( yearStr . Length == 4 && int . TryParse ( yearStr , out var year ) )
431
+ {
432
+ return new LicenseKey {
433
+ Expiry = new DateTime ( year + 1 , 1 , 1 , 0 , 0 , 0 , DateTimeKind . Utc ) ,
434
+ Hash = keySign ,
435
+ Name = keyText ,
436
+ Type = LicenseType . FreeOpenSource ,
437
+ } ;
438
+ }
439
+ }
440
+ }
441
+ catch { }
442
+
443
+ return null ;
444
+ }
280
445
281
446
public static void RemoveLicense ( )
282
447
{
@@ -382,6 +547,8 @@ public static LicenseFeature GetLicensedFeatures(this LicenseKey key)
382
547
case LicenseType . Free :
383
548
return LicenseFeature . Free ;
384
549
550
+ case LicenseType . FreeIndividual :
551
+ case LicenseType . FreeOpenSource :
385
552
case LicenseType . Indie :
386
553
case LicenseType . Business :
387
554
case LicenseType . Enterprise :
0 commit comments