{"id":27485371,"url":"https://github.com/nogipx/licensify","last_synced_at":"2025-04-16T17:51:13.950Z","repository":{"id":283840959,"uuid":"953057207","full_name":"nogipx/licensify","owner":"nogipx","description":"Secure license management for Dart/Flutter with ECDSA signatures, configurable encryption, and cross-platform support.","archived":false,"fork":false,"pushed_at":"2025-04-12T19:37:49.000Z","size":317,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-12T20:27:26.620Z","etag":null,"topics":["cryptography","dart","dartlang","digital-signature","ecdsa","library","license","license-management","licensing-library","software-licensing"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/licensify","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nogipx.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-03-22T13:24:43.000Z","updated_at":"2025-04-12T19:37:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"5a2b2eaf-8aa9-4563-acbf-b8f44c7d2bf6","html_url":"https://github.com/nogipx/licensify","commit_stats":null,"previous_names":["nogipx/licensify"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nogipx%2Flicensify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nogipx%2Flicensify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nogipx%2Flicensify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nogipx%2Flicensify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nogipx","download_url":"https://codeload.github.com/nogipx/licensify/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249261919,"owners_count":21239903,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cryptography","dart","dartlang","digital-signature","ecdsa","library","license","license-management","licensing-library","software-licensing"],"created_at":"2025-04-16T17:51:13.430Z","updated_at":"2025-04-16T17:51:13.939Z","avatar_url":"https://github.com/nogipx.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Licensify](https://img.shields.io/pub/v/licensify?label=Licensify\u0026labelColor=1A365D\u0026color=1A365D\u0026style=for-the-badge\u0026logo=dart)\n![More Projects](https://img.shields.io/badge/More_Projects-nogipx-FF6B35?style=for-the-badge\u0026labelColor=1A365D\u0026link=https://github.com/nogipx?tab=repositories)\n\n![GitHub stars](https://img.shields.io/github/stars/nogipx/licensify?style=flat-square\u0026labelColor=1A365D\u0026color=00A67E)\n![GitHub last commit](https://img.shields.io/github/last-commit/nogipx/licensify?style=flat-square\u0026labelColor=1A365D\u0026color=00A67E)\n![License](https://img.shields.io/badge/license-LGPL-blue.svg?style=flat-square\u0026labelColor=1A365D\u0026color=00A67E\u0026link=https://pub.dev/packages/licensify/license)\n\n\n# Licensify\n\nA lightweight yet powerful license management solution for Dart applications with cryptographically secure signatures.\n\n## Overview\n\nLicensify is a Dart library for license validation, signing, and management. It provides:\n\n- Cryptographically secure license validation\n- ECDSA signature support with legacy RSA key generation\n- License request generation and sharing\n- Platform-independent implementation\n- Command-line interface (CLI) for license management\n\n## 🚀 Contents\n\n- [Features](#-features)\n- [Installation](#-installation)\n- [Quick Start](#-quick-start)\n- [Usage Examples](#-usage-examples)\n- [CLI Tool](#-cli-tool)\n- [Documentation](#-documentation)\n- [License Request Generation](#license-request-generation)\n- [Security](#security)\n- [License](#license)\n\n## 🔥 Features\n\n- **Powerful Cryptography**: ECDSA with SHA-512 for robust protection\n- **Flexible Licenses**: Built-in and custom types, metadata, and features\n- **Expiration**: Automatic expiration verification\n- **Schema Validation**: Validate license structures with custom schemas\n- **Storage Independence**: Bring your own storage implementation\n- **Cross-Platform**: Works on all platforms including web (WASM)\n- **High Performance**: ECDSA up to 10x faster with 72% smaller key sizes\n\n## 📦 Installation\n\n```yaml\ndependencies:\n  licensify: ^2.0.0\n```\n\n## 🏁 Quick Start\n\n### ECDSA (recommended)\n\n```dart\n// 1. Generate key pair (server-side/developer only)\nfinal keyPair = EcdsaKeyGenerator.generateKeyPairAsPem(curve: EcCurve.p256);\n\n// 2. Create license (for your user)\nfinal license = keyPair.privateKey.licenseGenerator(\n  appId: 'com.example.app',\n  expirationDate: DateTime.now().add(Duration(days: 365)),\n  type: LicenseType.pro,\n);\n\n// 3. Validate license (client-side)\nfinal validator = keyPair.publicKey.licenseValidator;\nfinal result = validator.validateLicense(license);\nif (result.isValid) {\n  print('✅ License is valid');\n} else {\n  print('❌ License is invalid: ${result.message}');\n}\n```\n\n### Legacy RSA Key Generation\n\n```dart\n// While RSA keys can still be generated for backward compatibility,\n// they cannot be used for license operations in v2.0.0+\nfinal keyPair = RsaKeyGenerator.generateKeyPairAsPem(bitLength: 2048);\n\n// Note: Using RSA keys for license operations will throw UnsupportedError\n```\n\n## 📚 Usage Examples\n\n### Complete License Workflow\n\n```dart\n// SERVER: generating a license\n// Import private key \nfinal privateKey = LicensifyKeyImporter.importPrivateKeyFromString(privateKeyPem);\n// Note: Only ECDSA keys can be used for license operations\nfinal generator = privateKey.licenseGenerator;\n\nfinal license = generator(\n  appId: 'com.example.app',\n  expirationDate: DateTime.now().add(Duration(days: 365)),\n  type: LicenseType.pro,\n  features: {\n    'maxUsers': 50,\n    'modules': ['reporting', 'analytics', 'export'],\n    'premium': true,\n  },\n  metadata: {\n    'customerName': 'My Company',\n    'contactEmail': 'support@mycompany.com',\n  },\n);\n\n// Convert to bytes for transmission/storage\nfinal bytes = LicenseEncoder.encodeToBytes(license);\n\n// CLIENT: validating the received license\n// Import public key\nfinal publicKey = LicensifyKeyImporter.importPublicKeyFromString(publicKeyPem);\nfinal validator = publicKey.licenseValidator;\n\n// Read from bytes\nfinal receivedLicense = LicenseEncoder.decodeFromBytes(bytes);\n\n// Validate\nfinal result = validator.validateLicense(receivedLicense);\nif (result.isValid \u0026\u0026 !receivedLicense.isExpired) {\n  print('✅ License is valid - available level: ${receivedLicense.type.name}');\n} else {\n  print('❌ License is invalid or expired');\n}\n\n// Check license features\nif (receivedLicense.features?['premium'] == true) {\n  print('Premium features activated');\n}\n```\n\n### License Storage\n\n```dart\n// Built-in In-Memory storage\nfinal storage = InMemoryLicenseStorage();\nfinal repository = LicenseRepository(storage: storage);\n\n// Save license\nawait repository.saveLicense(license);\n\n// Retrieve current license\nfinal savedLicense = await repository.getCurrentLicense();\n\n// Custom storage implementation\nclass FileSystemLicenseStorage implements ILicenseStorage {\n  final String filePath;\n  \n  FileSystemLicenseStorage(this.filePath);\n  \n  @override\n  Future\u003cbool\u003e deleteLicenseData() async {\n    // Implementation to delete file\n    return true;\n  }\n  \n  @override\n  Future\u003cbool\u003e hasLicense() async {\n    // Implementation to check if file exists\n    return true;\n  }\n  \n  @override\n  Future\u003cUint8List?\u003e loadLicenseData() async {\n    // Implementation to read file\n    return null;\n  }\n  \n  @override\n  Future\u003cbool\u003e saveLicenseData(Uint8List data) async {\n    // Implementation to write to file\n    return true;\n  }\n}\n```\n\n### Schema Validation\n\n```dart\n// Define license schema\nfinal schema = LicenseSchema(\n  featureSchema: {\n    'maxUsers': SchemaField(\n      type: FieldType.integer,\n      required: true,\n      validators: [NumberValidator(minimum: 1, maximum: 1000)],\n    ),\n    'modules': SchemaField(\n      type: FieldType.array,\n      required: true,\n      validators: [\n        ArrayValidator(minItems: 1, itemValidator: StringValidator()),\n      ],\n    ),\n  },\n  metadataSchema: {\n    'customerName': SchemaField(\n      type: FieldType.string,\n      required: true,\n    ),\n  },\n  allowUnknownFeatures: false,\n  allowUnknownMetadata: true,\n);\n\n// Validate license against schema\nfinal schemaResult = validator.validateSchema(license, schema);\nif (schemaResult.isValid) {\n  print('✅ License schema is valid');\n} else {\n  print('❌ License schema is invalid:');\n  for (final entry in schemaResult.errors.entries) {\n    print('  - ${entry.key}: ${entry.value}');\n  }\n}\n\n// Comprehensive validation of signature, expiration, and schema\nfinal isValid = validator.validateLicenseWithSchema(license, schema);\n```\n\n## 🛠 CLI Tool\n\nLicensify includes a powerful command-line interface for managing licenses:\n\n```bash\n# Activate the package globally\ndart pub global activate licensify\n\n# Get help on available commands\nlicensify --help\n```\n\n### Available Commands\n\n```bash\n# Generate a key pair\nlicensify keygen --output ./keys --name app_keys\n\n# Create a license request (client side)\nlicensify request-create --appId com.example.app --publicKey ./keys/app.public.pem --output request.bin\n\n# Create a license request with custom extension\nlicensify request-create --appId com.example.app --publicKey ./keys/app.public.pem --output request.lreq --extension lreq\n\n# Decrypt and view a license request (server side)\nlicensify request-read --requestFile request.bin --privateKey ./keys/app.private.pem\n\n# Generate a license directly (server side)\nlicensify license-create --appId com.example.app --privateKey ./keys/app.private.pem --expiration \"2025-12-31\" --type pro --output license.licensify\n\n# Generate a license with custom extension\nlicensify license-create --appId com.example.app --privateKey ./keys/app.private.pem --expiration \"2025-12-31\" --type pro --extension lic --output license.lic\n\n# Respond to a license request (server side)\nlicensify license-respond --requestFile request.bin --privateKey ./keys/app.private.pem --expiration \"2025-12-31\" --type pro --output license.licensify\n\n# Verify a license\nlicensify license-verify --license license.licensify --publicKey ./keys/app.public.pem\n\n# Show license details\nlicensify license-read --license license.licensify\n```\n\n### CLI Features\n\n- **Comprehensive License Management**: Create, verify, and manage licenses\n- **License Plans**: Create and manage license plans with predefined parameters\n- **Custom License Types**: Define your own license types in plans\n- **Custom File Extensions**: Customize extensions for license and request files\n- **Trial Licenses**: Create and manage trial licenses with automatic expiration\n- **Plan-Based License Generation**: Create licenses based on predefined plans\n\n### License Request Generation (Client-side)\n\n```dart\nimport 'package:licensify/licensify.dart';\nimport 'dart:typed_data';\nimport 'dart:io';\n\n// Load your public key - IMPORTANT: Only ECDSA keys are supported in v2.0.0+\nfinal publicKeyString = '''\n-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n''';\nfinal publicKey = LicensifyKeyImporter.importPublicKeyFromString(publicKeyString);\n\n// Verify that the key is ECDSA\nif (publicKey.keyType != LicensifyKeyType.ecdsa) {\n  throw UnsupportedError('Only ECDSA keys are supported for license operations');\n}\n\n// Create a license request generator from the public key\nfinal generator = publicKey.licenseRequestGenerator(\n  // Optional: customize encryption parameters\n  aesKeySize: 256, \n  hkdfSalt: 'custom-salt',\n  hkdfInfo: 'license-request-info',\n);\n\n// Get device hash (in real app, implement proper device info collection)\nfinal deviceHash = await DeviceHashGenerator.getDeviceHash();\n\n// Generate a license request\nfinal encryptedBytes = generator(\n  deviceHash: deviceHash,\n  appId: 'com.example.app',\n  expirationHours: 48, // default is 48 hours\n);\n\n// Save the request to a file (simple example)\nfinal file = File('license_request.lreq');\nawait file.writeAsBytes(encryptedBytes);\nprint('License request saved to: ${file.path}');\n\n// In a real app, you would use the CLI command to generate this request:\n// licensify request-create --appId com.example.app --publicKey ./keys/app.public.pem --output request.lreq --extension lreq\n```\n\n### License Request Decryption (Server-side)\n\n```dart\nimport 'package:licensify/licensify.dart';\nimport 'dart:io';\nimport 'dart:typed_data';\n\n// Load the private key (server-side only)\nfinal privateKeyString = '''\n-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n''';\nfinal privateKey = LicensifyKeyImporter.importPrivateKeyFromString(privateKeyString);\n\n// Verify that the key is ECDSA\nif (privateKey.keyType != LicensifyKeyType.ecdsa) {\n  throw UnsupportedError('Only ECDSA keys are supported for license operations');\n}\n\n// Create a license request decrypter\nfinal decrypter = privateKey.licenseRequestDecrypter();\n\n// Read the encrypted request file\nfinal File requestFile = File('license_request.lreq');\nfinal Uint8List encryptedBytes = await requestFile.readAsBytes();\n\n// Decrypt the request\nfinal decryptedRequest = decrypter(encryptedBytes);\n\n// Access the request data\nprint('App ID: ${decryptedRequest.appId}');\nprint('Device Hash: ${decryptedRequest.deviceHash}');\nprint('Created At: ${decryptedRequest.createdAt}');\nprint('Expires At: ${decryptedRequest.expiresAt}');\n\n// In a real scenario, you would use the CLI commands:\n// licensify request-read --requestFile request.lreq --privateKey ./keys/app.private.pem\n// licensify license-respond --requestFile request.lreq --privateKey ./keys/app.private.pem --expiration \"2025-12-31\" --type pro --output license.licensify\n```\n\n## 🔒 Security\n\n1. **Private key** should be stored only on the server or licensing authority side\n2. **Public key** can be safely embedded in your application\n3. Code obfuscation is recommended in release builds\n4. ECDSA with P-256 curve provides high security level with smaller key sizes\n\n## 📝 License\n\n```\nSPDX-License-Identifier: LGPL-3.0-or-later\n```\n\nCreated by Karim \"nogipx\" Mamatkazin","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnogipx%2Flicensify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnogipx%2Flicensify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnogipx%2Flicensify/lists"}