licensify 1.7.1 copy "licensify: ^1.7.1" to clipboard
licensify: ^1.7.1 copied to clipboard

A lightweight yet powerful license management solution for Flutter and Dart applications with cryptographically secure signatures

Licensify More Projects

GitHub stars GitHub last commit License

Advanced licensing solution for Flutter/Dart applications with cryptographic protection.

Licensify transforms complex license management into a simple process, providing maximum security and flexibility.

🚀 Contents #

🔥 Features #

  • Powerful Cryptography: RSA and ECDSA with SHA-512 for robust protection
  • Flexible Licenses: Built-in and custom types, metadata, and features
  • Expiration: Automatic expiration verification
  • Schema Validation: Validate license structures with custom schemas
  • Storage Independence: Bring your own storage implementation
  • Cross-Platform: Works on all platforms including web (WASM)
  • High Performance: ECDSA up to 10x faster with 72% smaller key sizes

📦 Installation #

dependencies:
  licensify: ^1.7.0
copied to clipboard

🏁 Quick Start #

// 1. Generate key pair (server-side/developer only)
final keyPair = EcdsaKeyGenerator.generateKeyPairAsPem(curve: EcCurve.p256);

// 2. Create license (for your user)
final license = keyPair.privateKey.licenseGenerator(
  appId: 'com.example.app',
  expirationDate: DateTime.now().add(Duration(days: 365)),
  type: LicenseType.pro,
);

// 3. Validate license (client-side)
final validator = keyPair.publicKey.licenseValidator;
final result = validator.validateLicense(license);
if (result.isValid) {
  print('✅ License is valid');
} else {
  print('❌ License is invalid: ${result.message}');
}
copied to clipboard

RSA (traditional approach) #

// 1. Generate key pair
final keyPair = RsaKeyGenerator.generateKeyPairAsPem(bitLength: 2048);

// 2 and 3 - same as with ECDSA
copied to clipboard

📚 Usage Examples #

Complete License Workflow #

// SERVER: generating a license
// Import private key with automatic type detection
final privateKey = LicensifyKeyImporter.importPrivateKeyFromString(privateKeyPem);
final generator = privateKey.licenseGenerator;

final license = generator(
  appId: 'com.example.app',
  expirationDate: DateTime.now().add(Duration(days: 365)),
  type: LicenseType.pro,
  features: {
    'maxUsers': 50,
    'modules': ['reporting', 'analytics', 'export'],
    'premium': true,
  },
  metadata: {
    'customerName': 'My Company',
    'contactEmail': 'support@mycompany.com',
  },
);

// Convert to bytes for transmission/storage
final bytes = LicenseEncoder.encodeToBytes(license);

// CLIENT: validating the received license
// Import public key with automatic type detection
final publicKey = LicensifyKeyImporter.importPublicKeyFromString(publicKeyPem);
final validator = publicKey.licenseValidator;

// Read from bytes
final receivedLicense = LicenseEncoder.decodeFromBytes(bytes);

// Validate
final result = validator.validateLicense(receivedLicense);
if (result.isValid && !receivedLicense.isExpired) {
  print('✅ License is valid - available level: ${receivedLicense.type.name}');
} else {
  print('❌ License is invalid or expired');
}

// Check license features
if (receivedLicense.features?['premium'] == true) {
  print('Premium features activated');
}
copied to clipboard

License Storage #

// Built-in In-Memory storage
final storage = InMemoryLicenseStorage();
final repository = LicenseRepository(storage: storage);

// Save license
await repository.saveLicense(license);

// Retrieve current license
final savedLicense = await repository.getCurrentLicense();

// Custom storage implementation
class FileSystemLicenseStorage implements ILicenseStorage {
  final String filePath;
  
  FileSystemLicenseStorage(this.filePath);
  
  @override
  Future<bool> deleteLicenseData() async {
    // Implementation to delete file
    return true;
  }
  
  @override
  Future<bool> hasLicense() async {
    // Implementation to check if file exists
    return true;
  }
  
  @override
  Future<Uint8List?> loadLicenseData() async {
    // Implementation to read file
    return null;
  }
  
  @override
  Future<bool> saveLicenseData(Uint8List data) async {
    // Implementation to write to file
    return true;
  }
}
copied to clipboard

Schema Validation #

// Define license schema
final schema = LicenseSchema(
  featureSchema: {
    'maxUsers': SchemaField(
      type: FieldType.integer,
      required: true,
      validators: [NumberValidator(minimum: 1, maximum: 1000)],
    ),
    'modules': SchemaField(
      type: FieldType.array,
      required: true,
      validators: [
        ArrayValidator(minItems: 1, itemValidator: StringValidator()),
      ],
    ),
  },
  metadataSchema: {
    'customerName': SchemaField(
      type: FieldType.string,
      required: true,
    ),
  },
  allowUnknownFeatures: false,
  allowUnknownMetadata: true,
);

// Validate license against schema
final schemaResult = validator.validateSchema(license, schema);
if (schemaResult.isValid) {
  print('✅ License schema is valid');
} else {
  print('❌ License schema is invalid:');
  for (final entry in schemaResult.errors.entries) {
    print('  - ${entry.key}: ${entry.value}');
  }
}

// Comprehensive validation of signature, expiration, and schema
final isValid = validator.validateLicenseWithSchema(license, schema);
copied to clipboard

📖 Documentation #

Key Formats and Importing #

// Generate keys
final ecdsaKeyPair = EcdsaKeyGenerator.generateKeyPairAsPem();

// Create keys with explicit type specification
final privateKey = LicensifyPrivateKey.rsa(privateKeyPemString);
final publicKey = LicensifyPublicKey.ecdsa(publicKeyPemString);

// Import keys with automatic type detection
final privateKey = LicensifyKeyImporter.importPrivateKeyFromString(pemPrivateKey);
final publicKey = LicensifyKeyImporter.importPublicKeyFromString(pemPublicKey);

// Import keys from bytes
final privateKeyBytes = Uint8List.fromList(utf8.encode(privateKeyPem));
final privateKey = LicensifyKeyImporter.importPrivateKeyFromBytes(privateKeyBytes);

// Import key pair with auto type detection and compatibility check
final keyPair = LicensifyKeyImporter.importKeyPairFromStrings(
  privateKeyPem: privatePemString, 
  publicKeyPem: publicPemString,
);

// The importer automatically:
// 1. Detects key type (RSA or ECDSA)
// 2. Verifies key format correctness
// 3. Ensures key pair consistency (matching types)
copied to clipboard

License Types #

// Built-in types
final trial = LicenseType.trial;
final standard = LicenseType.standard;
final pro = LicenseType.pro;

// Custom types
final enterprise = LicenseType('enterprise');
final premium = LicenseType('premium');
copied to clipboard

License Format #

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "appId": "com.example.app",
  "createdAt": "2024-07-25T14:30:00Z",
  "expirationDate": "2025-07-25T14:30:00Z",
  "type": "pro",
  "features": {
    "maxUsers": 50,
    "modules": ["analytics", "reporting"]
  },
  "metadata": {
    "customerName": "My Company"
  },
  "signature": "Base64EncodedSignature..."
}
copied to clipboard

🔒 Security #

  1. Private key should be stored only on the server or licensing authority side
  2. Public key can be safely embedded in your application
  3. Code obfuscation is recommended in release builds
  4. ECDSA with P-256 curve provides high security level with smaller key sizes

📝 License #

SPDX-License-Identifier: LGPL-3.0-or-later
copied to clipboard

Created by Karim "nogipx" Mamatkazin

4
likes
160
points
415
downloads

Publisher

verified publisherdart.nogipx.dev

Weekly Downloads

2024.09.16 - 2025.03.31

A lightweight yet powerful license management solution for Flutter and Dart applications with cryptographically secure signatures

Repository (GitHub)
View/report issues

Topics

#license #security #validation #cryptography #web

Documentation

Documentation
API reference

License

LGPL-3.0 (license)

Dependencies

basic_utils, pointycastle, uuid

More

Packages that depend on licensify