33
44using System ;
55using System . Collections . Generic ;
6+ using System . ComponentModel ;
67using System . Globalization ;
78using System . IO ;
89using System . Linq ;
@@ -22,6 +23,8 @@ public LicenseException(string message, Exception innerException) : base(message
2223 public enum LicenseType
2324 {
2425 Free ,
26+ FreeIndividual ,
27+ FreeOpenSource ,
2528 Indie ,
2629 Business ,
2730 Enterprise ,
@@ -210,6 +213,12 @@ public static void RegisterLicense(string licenseKeyText)
210213 Thread . CurrentThread . CurrentCulture = CultureInfo . InvariantCulture ;
211214 try
212215 {
216+ if ( IsFreeLicenseKey ( licenseKeyText ) )
217+ {
218+ ValidateFreeLicenseKey ( licenseKeyText ) ;
219+ return ;
220+ }
221+
213222 var parts = licenseKeyText . SplitOnFirst ( '-' ) ;
214223 subId = parts [ 0 ] ;
215224
@@ -227,7 +236,7 @@ public static void RegisterLicense(string licenseKeyText)
227236 catch ( Exception ex )
228237 {
229238 //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 )
231240 throw ;
232241
233242 if ( ex is LicenseException )
@@ -277,6 +286,162 @@ private static void ValidateLicenseKey(LicenseKey key)
277286 if ( LicenseWarningMessage != null )
278287 Console . WriteLine ( LicenseWarningMessage ) ;
279288 }
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+ }
280445
281446 public static void RemoveLicense ( )
282447 {
@@ -382,6 +547,8 @@ public static LicenseFeature GetLicensedFeatures(this LicenseKey key)
382547 case LicenseType . Free :
383548 return LicenseFeature . Free ;
384549
550+ case LicenseType . FreeIndividual :
551+ case LicenseType . FreeOpenSource :
385552 case LicenseType . Indie :
386553 case LicenseType . Business :
387554 case LicenseType . Enterprise :
0 commit comments