├── .github ├── FUNDING.yml ├── pull_request_template.md └── workflows │ ├── dart.yml │ └── publish.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example ├── README.md ├── ec_signature_example.dart ├── rsa_crypto_example.dart └── rsa_signature_example.dart ├── lib ├── crypton.dart └── src │ ├── ec │ ├── ecpoint.dart │ ├── helper.dart │ ├── keypair.dart │ ├── keypair_factory.dart │ ├── private_key.dart │ └── public_key.dart │ ├── keypair.dart │ ├── keypair_factory.dart │ ├── private_key.dart │ ├── public_key.dart │ └── rsa │ ├── keypair.dart │ ├── keypair_factory.dart │ ├── private_key.dart │ └── public_key.dart ├── pubspec.yaml └── test └── crypton_test.dart /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: konstantinullrich 2 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Pull Request Checklist 2 | 3 | * [ ] My commits are in nice logical chunks with [good commit messages](http://chris.beams.io/posts/git-commit/) 4 | * [ ] My changes are [rebased](http://blog.axosoft.com/golden-rule-of-rebasing-in-git/) on the latest [`trunk`](https://github.com/konstantinullrich/crypton/tree/trunk) branch 5 | * [ ] Ran ´dart format lib test example´ 6 | * [ ] All tests are passing 7 | * [ ] My changes are ready to be shipped to users 8 | 9 | 10 | ## Issues affected 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | name: Dart CI 2 | 3 | on: 4 | push: 5 | branches: [ trunk ] 6 | pull_request: 7 | branches: [ trunk ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: dart-lang/setup-dart@v1 15 | with: 16 | sdk: stable 17 | 18 | - name: Install dependencies 19 | run: dart pub get 20 | 21 | - name: Run tests 22 | run: dart pub run test 23 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to pub.dev 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | publishing: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Publish package 18 | uses: k-paxian/dart-package-publisher@v1.6 19 | with: 20 | accessToken: ${{ secrets.GOOGLE_OAUTH_ACCESS_TOKEN }} 21 | refreshToken: ${{ secrets.GOOGLE_OAUTH_REFRESH_TOKEN }} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | .pub/ 7 | build/ 8 | # If you're building an application, you may want to check-in your pubspec.lock 9 | pubspec.lock 10 | 11 | # Directory created by dartdoc 12 | # If you don't generate documentation locally you can remove this line. 13 | doc/api/ 14 | 15 | # IntelliJ 16 | .idea/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.2.1 2 | - Update dependencies 3 | - Update Readme 4 | 5 | ## 2.2.0 6 | - Update `asn1lib` dependency to ^1.5.0 and fix the resulting lint warnings 7 | 8 | ## 2.1.0 9 | - Add `KeypairFactory`, `RSAKeypairFactory` and `ECKeypairFactory` 10 | - Update dependencies 11 | 12 | ## 2.0.5 13 | - Update Example Doc 14 | - Minor Bugfixes 15 | 16 | ## 2.0.4 17 | - Update dependencies 18 | 19 | ## 2.0.3 20 | - Update dependencies 21 | 22 | ## 2.0.2 23 | - Update dependencies 24 | 25 | ## 2.0.1 26 | - Update Code Format 27 | - Update Tests 28 | 29 | ## 2.0.0 30 | - Migrate to support null safety 31 | 32 | ## 1.1.3 33 | - Update deprecated PointyCastle features 34 | 35 | ## 1.1.2 36 | - Update dependencies 37 | - Update tests 38 | 39 | ## 1.1.1 40 | - Insure backwards compatibility of `RSAPrivateKey.createSignature` and `RSAPublicKey.verifySignature` 41 | - Update dependencies 42 | 43 | ## 1.1.0 44 | - Add RSA key formatting 45 | - Add SHA-512 signing and verification 46 | - Fix RSA encryption/decryption 47 | - Insure backwards compatibility 48 | Thank you [jld3103](https://github.com/jld3103) for contributing 49 | 50 | ## 1.0.9 51 | - The length of the RSAPrivateKey is now adjustable thanks to [melewetwo](https://github.com/melewetwo) 52 | - Added more documentation 53 | 54 | ## 1.0.8 55 | - Update dependencies 56 | 57 | ## 1.0.7 58 | - Add toPem and fromPem to RSAPublicKey 59 | - Add toPem and fromPem to RSAPrivateKey 60 | 61 | ## 1.0.6 62 | - Update dependencies 63 | 64 | ## 1.0.5 65 | - Add more Documentation and fix a small bug 66 | 67 | ## 1.0.4 68 | - Styled Code using new Guidelines 69 | 70 | ## 1.0.3 71 | - Fixed Typos in README 72 | 73 | ## 1.0.2 74 | - Add Documentation 75 | 76 | ## 1.0.1 77 | - Add Documentation 78 | 79 | ## 1.0.0 80 | - Update README, Ready for Version 1.0.0 81 | 82 | ## 0.0.5 83 | - Update Code Format 84 | 85 | ## 0.0.4 86 | - Add ECKeypair with Random Key-generation 87 | - Add ECSignature creation and Signature validation 88 | 89 | ## 0.0.3 90 | - Update Code Format 91 | 92 | ## 0.0.2 93 | - Add Example overview 94 | 95 | ## 0.0.1 96 | - Add RSAKeypair with Random Key-generation 97 | - Add RSASignature creation and Signature validation 98 | - Add encryption using RSA 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Konstantin Ullrich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crypton 2 | A simple Dart library for asymmetric encryption and digital signatures 3 | 4 | > This library is a simplified interface to interact with the [pointy castle](https://pub.dev/packages/pointycastle) 5 | 6 | For symmetric encryption and hashing I strongly recommend to use [steel_crypt](https://pub.dev/packages/steel_crypt). 7 | 8 | ## Features 9 | #### RSA 10 | - Generate a random RSAKeypair 11 | - Sign Strings 12 | - Validate Signatures 13 | - Encrypt and Decrypt Strings 14 | 15 | #### EC 16 | - Generate a random ECKeypair 17 | - Sign Strings 18 | - Validate Signatures 19 | 20 | ## See more 21 | To see more take a look at the examples 22 | 23 | ## Contact 24 | If you want a specific Feature implemented feel free to 25 | create a Pull Request on the [Crypton GitHub](https://github.com/konstantinullrich/crypton) 26 | or tweet at me [@konstiullrich](https://twitter.com/konstiullrich) (You can also tweet at me just to say "hi") 27 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. 3 | include: package:lints/recommended.yaml 4 | 5 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 6 | # Uncomment to specify additional rules. 7 | # linter: 8 | # rules: 9 | # - camel_case_types 10 | 11 | analyzer: 12 | # exclude: 13 | # - path/to/excluded/files/** 14 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## Encryption and Decryption 4 | ````dart 5 | RSAKeypair rsaKeypair = RSAKeypair.fromRandom(); 6 | String message = DateTime.now().millisecondsSinceEpoch.toRadixString(16); 7 | 8 | String encrypted = rsaKeypair.publicKey.encrypt(message); 9 | String decrypted = rsaKeypair.privateKey.decrypt(encrypted); 10 | ```` 11 | 12 | ## Signing and Verifying 13 | ##### Using RSA 14 | ````dart 15 | RSAKeypair rsaKeypair = RSAKeypair.fromRandom(); 16 | String message = DateTime.now().millisecondsSinceEpoch.toRadixString(16); 17 | 18 | String signature = rsaKeypair.privateKey.createSHA256Signature(utf8.encode(message) as Uint8List); 19 | bool verified = rsaKeypair.publicKey.verifySHA256Signature(message, signature); 20 | ```` 21 | ##### Using EC 22 | ````dart 23 | ECKeypair ecKeypair = ECKeypair.fromRandom(); 24 | String message = DateTime.now().millisecondsSinceEpoch.toRadixString(16); 25 | 26 | String signature = ecKeypair.privateKey.createSHA256Signature(utf8.encode(message) as Uint8List); 27 | bool verified = ecKeypair.publicKey.verifySHA256Signature(message, signature); 28 | ```` 29 | 30 | -------------------------------------------------------------------------------- /example/ec_signature_example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:crypton/crypton.dart'; 5 | 6 | void main() { 7 | final ecKeypair = ECKeypair.fromRandom(); 8 | final message = 9 | utf8.encode(DateTime.now().millisecondsSinceEpoch.toRadixString(16)); 10 | 11 | final privateKeyString = ecKeypair.privateKey.toString(); 12 | final publicKeyString = ecKeypair.publicKey.toString(); 13 | final signature = 14 | ecKeypair.privateKey.createSHA256Signature(message as Uint8List); 15 | final verified = 16 | ecKeypair.privateKey.publicKey.verifySHA256Signature(message, signature); 17 | 18 | print('Your Private Key\n $privateKeyString\n---'); 19 | print('Your Public Key\n $publicKeyString\n---'); 20 | 21 | if (verified) { 22 | print('The Signature is verified!'); 23 | } else { 24 | print('The Signature could not be verified!'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/rsa_crypto_example.dart: -------------------------------------------------------------------------------- 1 | import 'package:crypton/crypton.dart'; 2 | 3 | void main() { 4 | final rsaKeypair = RSAKeypair.fromRandom(); 5 | final message = DateTime.now().millisecondsSinceEpoch.toRadixString(16); 6 | 7 | final privateKeyString = rsaKeypair.privateKey.toString(); 8 | final publicKeyString = rsaKeypair.publicKey.toString(); 9 | final encrypted = rsaKeypair.publicKey.encrypt(message); 10 | final decrypted = rsaKeypair.privateKey.decrypt(encrypted); 11 | 12 | print('Your Private Key\n $privateKeyString\n---'); 13 | print('Your Public Key\n $publicKeyString\n---'); 14 | print('Encrypted Message\n $encrypted\n---'); 15 | print('Decrypted Message\n $decrypted\n---'); 16 | 17 | if (decrypted == message) { 18 | print('The Message was successfully decrypted!'); 19 | } else { 20 | print('Failed to decrypted the Message!'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/rsa_signature_example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:crypton/crypton.dart'; 5 | 6 | void main() { 7 | final rsaKeypair = RSAKeypair.fromRandom(); 8 | final message = 9 | utf8.encode(DateTime.now().millisecondsSinceEpoch.toRadixString(16)); 10 | 11 | final privateKeyString = rsaKeypair.privateKey.toString(); 12 | final publicKeyString = rsaKeypair.publicKey.toString(); 13 | final signature = 14 | rsaKeypair.privateKey.createSHA256Signature(message as Uint8List); 15 | final verified = 16 | rsaKeypair.publicKey.verifySHA256Signature(message, signature); 17 | 18 | print('Your Private Key\n $privateKeyString\n---'); 19 | print('Your Public Key\n $publicKeyString\n---'); 20 | 21 | if (verified) { 22 | print('The Signature is verified!'); 23 | } else { 24 | print('The Signature could not be verified!'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/crypton.dart: -------------------------------------------------------------------------------- 1 | library crypton; 2 | 3 | export 'src/private_key.dart'; 4 | export 'src/public_key.dart'; 5 | export 'src/keypair.dart'; 6 | export 'src/keypair_factory.dart'; 7 | 8 | /// Rivest-Shamir-Adleman Cryptography (RSA) 9 | /// 10 | /// [RSAPrivateKey] can be used to sign and decrypt data using the RSA Algorithm 11 | /// [RSAPublicKey] can be used to verify signatures and encrypt data using RSA 12 | /// [RSAKeypair] is used to generate a pair of a [RSAPrivateKey] and a [RSAPublicKey] 13 | /// [RSAKeypairFactory] is used to generate a [RSAKeypair] 14 | export 'src/rsa/private_key.dart'; 15 | export 'src/rsa/public_key.dart'; 16 | export 'src/rsa/keypair.dart'; 17 | export 'src/rsa/keypair_factory.dart'; 18 | 19 | /// Elliptic Curve Cryptography (ECC) 20 | /// 21 | /// [ECPrivateKey] can be used to sign [String]s using ECC 22 | /// [ECPublicKey] can be used to verify signatures using ECC 23 | /// [ECKeypair] is used to generate a pair of a [ECPrivateKey] and a [ECPublicKey] 24 | /// [ECKeypairFactory] is used to generate a [ECKeypair] 25 | /// [ECPoint] is a Point on the elliptic Curve 26 | export 'src/ec/private_key.dart'; 27 | export 'src/ec/public_key.dart'; 28 | export 'src/ec/keypair.dart'; 29 | export 'src/ec/keypair_factory.dart'; 30 | export 'src/ec/ecpoint.dart'; 31 | -------------------------------------------------------------------------------- /lib/src/ec/ecpoint.dart: -------------------------------------------------------------------------------- 1 | import 'package:pointycastle/export.dart' as pointy; 2 | 3 | /// Point on the elliptic Curve 4 | class ECPoint { 5 | final BigInt x; 6 | final BigInt y; 7 | final bool withCompression; 8 | static final pointy.ECDomainParameters curve = pointy.ECCurve_secp256k1(); 9 | 10 | /// Create an [ECPoint] on a elliptic Curve 11 | ECPoint(this.x, this.y, [this.withCompression = false]); 12 | 13 | /// Export a [ECPoint] as Pointy Castle ECPoint 14 | pointy.ECPoint get asPointyCastle => 15 | curve.curve.createPoint(x, y, withCompression); 16 | 17 | ECPoint operator *(BigInt k) { 18 | final point = (asPointyCastle * k)!; 19 | return ECPoint( 20 | point.x!.toBigInteger()!, point.y!.toBigInteger()!, point.isCompressed); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/ec/helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:pointycastle/export.dart'; 5 | 6 | ParametersWithRandom withRandom(ECKeyGeneratorParameters keyParams) { 7 | final randomSeed = Random.secure(); 8 | final seed = Uint8List.fromList( 9 | List.generate(32, (_) => randomSeed.nextInt(256))); 10 | final fortunaRandom = FortunaRandom(); 11 | fortunaRandom.seed(KeyParameter(seed)); 12 | return ParametersWithRandom(keyParams, fortunaRandom); 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/ec/keypair.dart: -------------------------------------------------------------------------------- 1 | import 'package:crypton/crypton.dart'; 2 | import 'package:pointycastle/export.dart' as pointy; 3 | 4 | import 'helper.dart'; 5 | 6 | /// [Keypair] using EC Algorithm 7 | class ECKeypair implements Keypair { 8 | late ECPrivateKey _privateKey; 9 | late ECPublicKey _publicKey; 10 | 11 | /// Create a [ECKeypair] using an [ECPrivateKey] 12 | ECKeypair(this._privateKey) : _publicKey = _privateKey.publicKey; 13 | 14 | /// Generate a random [ECKeypair] on the secp256k1-Curve 15 | ECKeypair.fromRandom() { 16 | final keyParams = 17 | pointy.ECKeyGeneratorParameters(pointy.ECCurve_secp256k1()); 18 | 19 | final generator = pointy.ECKeyGenerator(); 20 | generator.init(withRandom(keyParams)); 21 | 22 | final pair = generator.generateKeyPair(); 23 | final publicKey = pair.publicKey as pointy.ECPublicKey; 24 | final privateKey = pair.privateKey as pointy.ECPrivateKey; 25 | 26 | final Q = publicKey.Q!; 27 | _publicKey = ECPublicKey(Q.x!.toBigInteger()!, Q.y!.toBigInteger()!); 28 | _privateKey = ECPrivateKey(privateKey.d!); 29 | } 30 | 31 | /// Get the [ECPublicKey] associated [ECPrivateKey] 32 | @override 33 | ECPublicKey get publicKey => _publicKey; 34 | 35 | /// Get the [ECPrivateKey] associated [ECPublicKey] 36 | @override 37 | ECPrivateKey get privateKey => _privateKey; 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/ec/keypair_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:crypton/crypton.dart'; 2 | 3 | /// [KeypairFactory] using the EC Algorithm 4 | class ECKeypairFactory implements KeypairFactory { 5 | /// Create a fresh [ECKeypairFactory] 6 | ECKeypairFactory(); 7 | 8 | /// Generate a random [ECKeypair] 9 | @override 10 | ECKeypair fromRandom() => ECKeypair.fromRandom(); 11 | 12 | /// Generate a random [ECKeypair] asynchronously 13 | @override 14 | Future fromRandomAsync() async => 15 | Future(() => ECKeypair.fromRandom()); 16 | } 17 | -------------------------------------------------------------------------------- /lib/src/ec/private_key.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:crypton/crypton.dart'; 5 | import 'package:pointycastle/export.dart' as pointy; 6 | 7 | /// [PrivateKey] using EC Algorithm 8 | class ECPrivateKey implements PrivateKey { 9 | final pointy.ECPrivateKey _privateKey; 10 | static final pointy.ECDomainParameters curve = pointy.ECCurve_secp256k1(); 11 | 12 | /// Create an [ECPrivateKey] for the given d parameter. 13 | ECPrivateKey(BigInt d) : _privateKey = pointy.ECPrivateKey(d, curve); 14 | 15 | /// Create an [ECPrivateKey] from the given String. 16 | ECPrivateKey.fromString(String privateKeyString) 17 | : _privateKey = pointy.ECPrivateKey( 18 | BigInt.parse(privateKeyString, radix: 16), curve); 19 | 20 | /// Sign an message with SHA-256 which can be verified using the associated [ECPublicKey] 21 | @override 22 | @Deprecated('Use createSHA256Signature for creating SHA-256 signatures') 23 | String createSignature(String message) => 24 | utf8.decode(createSHA256Signature(utf8.encode(message) as Uint8List)); 25 | 26 | /// Sign an message with SHA-256 which can be verified using the associated [ECPublicKey] 27 | @override 28 | Uint8List createSHA256Signature(Uint8List message) => 29 | _createSignature(message, 'SHA-256/DET-ECDSA'); 30 | 31 | /// Sign an message with SHA-512 which can be verified using the associated [ECPublicKey] 32 | @override 33 | Uint8List createSHA512Signature(Uint8List message) => 34 | _createSignature(message, 'SHA-512/DET-ECDSA'); 35 | 36 | Uint8List _createSignature(Uint8List message, String algorithm) { 37 | final signer = pointy.Signer(algorithm); 38 | pointy.AsymmetricKeyParameter privateKeyParams = 39 | pointy.PrivateKeyParameter(_privateKey); 40 | signer.init(true, privateKeyParams); 41 | final sig = signer.generateSignature(message) as pointy.ECSignature; 42 | return utf8.encode(sig.r.toRadixString(16) + sig.s.toRadixString(16)) 43 | as Uint8List; 44 | } 45 | 46 | /// Get the [ECPublicKey] of the [ECPrivateKey] 47 | @override 48 | ECPublicKey get publicKey { 49 | final Q = (curve.G * d)!; 50 | return ECPublicKey(Q.x!.toBigInteger()!, Q.y!.toBigInteger()!); 51 | } 52 | 53 | /// Get the d Parameter as [BigInt] 54 | BigInt get d => _privateKey.d!; 55 | 56 | /// Export a [ECPrivateKey] as Pointy Castle ECPrivateKey 57 | @override 58 | pointy.ECPrivateKey get asPointyCastle => _privateKey; 59 | 60 | /// Export a [ECPrivateKey] as String which can be reversed using [ECPrivateKey.fromString]. 61 | @override 62 | String toString() => d.toRadixString(16); 63 | } 64 | -------------------------------------------------------------------------------- /lib/src/ec/public_key.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:crypton/crypton.dart'; 5 | import 'package:pointycastle/export.dart' as pointy; 6 | 7 | /// [PublicKey] using EC Algorithm 8 | class ECPublicKey implements PublicKey { 9 | final pointy.ECPublicKey _publicKey; 10 | static final pointy.ECDomainParameters curve = pointy.ECCurve_secp256k1(); 11 | 12 | /// Create an [ECPublicKey] for the given coordinates. 13 | ECPublicKey(BigInt x, BigInt y) 14 | : _publicKey = 15 | pointy.ECPublicKey(ECPoint(x, y, true).asPointyCastle, curve); 16 | 17 | /// Create an [ECPublicKey] from the given String. 18 | ECPublicKey.fromString(String publicKeyString) 19 | : _publicKey = pointy.ECPublicKey( 20 | curve.curve.decodePoint(base64Decode(publicKeyString)), curve); 21 | 22 | /// Verify the signature of a SHA256-hashed message signed with the associated [ECPrivateKey] 23 | @Deprecated('For SHA256 signature verification use verifySHA256Signature') 24 | @override 25 | bool verifySignature(String message, String signature) => 26 | verifySHA256Signature(utf8.encode(message) as Uint8List, 27 | utf8.encode(signature) as Uint8List); 28 | 29 | /// Verify the signature of a SHA256-hashed message signed with the associated [ECPrivateKey] 30 | @override 31 | bool verifySHA256Signature(Uint8List message, Uint8List signature) => 32 | _verifySignature(message, signature, 'SHA-256/DET-ECDSA'); 33 | 34 | /// Verify the signature of a SHA512-hashed message signed with the associated [ECPrivateKey] 35 | @override 36 | bool verifySHA512Signature(Uint8List message, Uint8List signature) => 37 | _verifySignature(message, signature, 'SHA-512/DET-ECDSA'); 38 | 39 | bool _verifySignature( 40 | Uint8List message, Uint8List signatureString, String algorithm) { 41 | final sigLength = (signatureString.length / 2).round(); 42 | final r = BigInt.parse( 43 | utf8.decode(signatureString.sublist(0, sigLength)), 44 | radix: 16, 45 | ); 46 | final s = BigInt.parse( 47 | utf8.decode(signatureString.sublist(sigLength)), 48 | radix: 16, 49 | ); 50 | final signature = pointy.ECSignature(r, s); 51 | final signer = pointy.Signer(algorithm); 52 | signer.init(false, pointy.PublicKeyParameter(_publicKey)); 53 | return signer.verifySignature(message, signature); 54 | } 55 | 56 | /// Get [ECPoint] Q, which is the Public Point 57 | ECPoint get Q => ECPoint(_publicKey.Q!.x!.toBigInteger()!, 58 | _publicKey.Q!.y!.toBigInteger()!, _publicKey.Q!.isCompressed); 59 | 60 | /// Export a [ECPublicKey] as Pointy Castle ECPublicKey 61 | @override 62 | pointy.ECPublicKey get asPointyCastle => _publicKey; 63 | 64 | /// Export a [ECPublicKey] as String which can be reversed using [ECPublicKey.fromString]. 65 | @override 66 | String toString() => base64Encode(_publicKey.Q!.getEncoded()); 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/keypair.dart: -------------------------------------------------------------------------------- 1 | import 'package:crypton/crypton.dart'; 2 | 3 | abstract class Keypair { 4 | /// Create a [Keypair] using an [PrivateKey] 5 | Keypair(PrivateKey privateKey); 6 | 7 | /// Generate a random [Keypair] 8 | Keypair.fromRandom(); 9 | 10 | /// Get the [PublicKey] associated [PrivateKey] 11 | PublicKey get publicKey => 12 | throw UnimplementedError('publicKey is not implemented yet!'); 13 | 14 | /// Get the [PrivateKey] associated [PublicKey] 15 | PrivateKey get privateKey => 16 | throw UnimplementedError('privateKey is not implemented yet!'); 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/keypair_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:crypton/crypton.dart'; 2 | 3 | abstract class KeypairFactory { 4 | /// Create a fresh [KeypairFactory] 5 | KeypairFactory(); 6 | 7 | /// Generate a random [Keypair] 8 | Keypair fromRandom(); 9 | 10 | /// Generate a random [Keypair] asynchronously 11 | Future fromRandomAsync(); 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/private_key.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:crypton/crypton.dart'; 4 | import 'package:pointycastle/export.dart' as pointy; 5 | 6 | abstract class PrivateKey { 7 | PrivateKey(); 8 | 9 | /// Create an [PrivateKey] from the given String. 10 | PrivateKey.fromString(String privateKeyString); 11 | 12 | /// Sign an message which can be verified using the associated [PublicKey] 13 | @Deprecated('Use createSHA256Signature for creating SHA-256 signatures') 14 | String createSignature(String message) => throw UnimplementedError( 15 | 'createSignature(String message) is not implemented yet!'); 16 | 17 | /// Create a SHA-256 of a [message] 18 | Uint8List createSHA256Signature(Uint8List message) => 19 | throw UnimplementedError( 20 | 'createSHA256Signature(Uint8List message) is not implemented yet!'); 21 | 22 | /// Create a SHA-512 of a [message] 23 | Uint8List createSHA512Signature(Uint8List message) => 24 | throw UnimplementedError( 25 | 'createSHA512Signature(Uint8List message) is not implemented yet!'); 26 | 27 | /// Get the [PublicKey] of the [PrivateKey] 28 | PublicKey get publicKey => 29 | throw UnimplementedError('publicKey is not implemented yet!'); 30 | 31 | /// Export a [PrivateKey] as Pointy Castle PrivateKey 32 | pointy.PrivateKey get asPointyCastle => 33 | throw UnimplementedError('asPointyCastle is not implemented yet!'); 34 | 35 | /// Export a [PrivateKey] as String which can be reversed using [PrivateKey.fromString]. 36 | @override 37 | String toString() => super.toString(); 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/public_key.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:pointycastle/export.dart' as pointy; 4 | 5 | abstract class PublicKey { 6 | PublicKey(); 7 | 8 | /// Create an [PublicKey] from the given String. 9 | PublicKey.fromString(String publicKeyString); 10 | 11 | /// Verify the signature of a SHA256-hashed message signed with the associated [PrivateKey] 12 | @Deprecated('For SHA256 signature verification use verifySHA256Signature') 13 | bool verifySignature(String message, String signature) => 14 | throw UnimplementedError( 15 | 'verifySignature(String message, String signature) is not implemented yet!'); 16 | 17 | /// Verify the signature of a SHA256-hashed message signed with the associated [PrivateKey] 18 | bool verifySHA256Signature(Uint8List message, Uint8List signature) => 19 | throw UnimplementedError( 20 | 'verifySHA256Signature(Uint8List message, Uint8List signature) is not implemented yet!'); 21 | 22 | /// Verify the signature of a SHA512-hashed message signed with the associated [PrivateKey] 23 | bool verifySHA512Signature(Uint8List message, Uint8List signature) => 24 | throw UnimplementedError( 25 | 'verifySHA512Signature(Uint8List message, Uint8List signature) is not implemented yet!'); 26 | 27 | /// Export a [PublicKey] as Pointy Castle PublicKey 28 | pointy.PublicKey get asPointyCastle => 29 | throw UnimplementedError('asPointyCastle is not implemented yet!'); 30 | 31 | /// Export a [PublicKey] as String which can be reversed using [PublicKey.fromString]. 32 | @override 33 | String toString() => super.toString(); 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/rsa/keypair.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:pointycastle/export.dart' as pointy; 5 | import 'package:crypton/crypton.dart'; 6 | 7 | /// [Keypair] using RSA Algorithm 8 | class RSAKeypair implements Keypair { 9 | late RSAPrivateKey _privateKey; 10 | late RSAPublicKey _publicKey; 11 | 12 | /// Create a [RSAKeypair] using an [RSAPrivateKey] 13 | RSAKeypair(this._privateKey) : _publicKey = _privateKey.publicKey; 14 | 15 | /// Generate a random [RSAKeypair] with a default key size of 2048 bit 16 | /// 17 | /// The recommended key size is 4096 but was changed to 2048 to insure backwards comparability 18 | RSAKeypair.fromRandom({int keySize = 2048}) { 19 | final keyParams = 20 | pointy.RSAKeyGeneratorParameters(BigInt.parse('65537'), keySize, 12); 21 | 22 | final fortunaRandom = pointy.FortunaRandom(); 23 | final random = Random.secure(); 24 | final seeds = []; 25 | for (var i = 0; i < 32; i++) { 26 | seeds.add(random.nextInt(255)); 27 | } 28 | fortunaRandom.seed(pointy.KeyParameter(Uint8List.fromList(seeds))); 29 | 30 | final randomParams = pointy.ParametersWithRandom(keyParams, fortunaRandom); 31 | final generator = pointy.RSAKeyGenerator(); 32 | generator.init(randomParams); 33 | 34 | final pair = generator.generateKeyPair(); 35 | final publicKey = pair.publicKey as pointy.RSAPublicKey; 36 | final privateKey = pair.privateKey as pointy.RSAPrivateKey; 37 | 38 | _publicKey = RSAPublicKey(publicKey.modulus!, publicKey.exponent!); 39 | _privateKey = RSAPrivateKey(privateKey.modulus!, privateKey.exponent!, 40 | privateKey.p!, privateKey.q!); 41 | } 42 | 43 | /// Get the [RSAPublicKey] associated [RSAPrivateKey] 44 | @override 45 | RSAPublicKey get publicKey => _publicKey; 46 | 47 | /// Get the [RSAPrivateKey] associated [RSAPublicKey] 48 | @override 49 | RSAPrivateKey get privateKey => _privateKey; 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/rsa/keypair_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:crypton/crypton.dart'; 2 | 3 | /// [KeypairFactory] using the RSA Algorithm 4 | class RSAKeypairFactory implements KeypairFactory { 5 | /// Create a fresh [RSAKeypairFactory] 6 | RSAKeypairFactory(); 7 | 8 | /// Generate a random [RSAKeypair] 9 | @override 10 | RSAKeypair fromRandom({int keySize = 2048}) => 11 | RSAKeypair.fromRandom(keySize: keySize); 12 | 13 | /// Generate a random [RSAKeypair] asynchronously 14 | @override 15 | Future fromRandomAsync({int keySize = 2048}) async => 16 | Future(() => RSAKeypair.fromRandom(keySize: keySize)); 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/rsa/private_key.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:asn1lib/asn1lib.dart'; 5 | import 'package:crypton/crypton.dart'; 6 | import 'package:pointycastle/export.dart' as pointy; 7 | 8 | /// [PrivateKey] using RSA Algorithm 9 | class RSAPrivateKey implements PrivateKey { 10 | late pointy.RSAPrivateKey _privateKey; 11 | 12 | /// Create an [RSAPrivateKey] for the given parameters. 13 | RSAPrivateKey(BigInt modulus, BigInt exponent, BigInt p, BigInt q) 14 | : _privateKey = pointy.RSAPrivateKey(modulus, exponent, p, q); 15 | 16 | /// Create an [RSAPrivateKey] from the given String. 17 | RSAPrivateKey.fromString(String privateKeyString) { 18 | List privateKeyDER = base64Decode(privateKeyString); 19 | var asn1Parser = ASN1Parser(privateKeyDER as Uint8List); 20 | final topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; 21 | final privateKey = topLevelSeq.elements[2]; 22 | 23 | asn1Parser = ASN1Parser(privateKey.contentBytes()); 24 | final pkSeq = asn1Parser.nextObject() as ASN1Sequence; 25 | 26 | final modulus = pkSeq.elements[1] as ASN1Integer; 27 | final privateExponent = pkSeq.elements[3] as ASN1Integer; 28 | final p = pkSeq.elements[4] as ASN1Integer; 29 | final q = pkSeq.elements[5] as ASN1Integer; 30 | 31 | _privateKey = pointy.RSAPrivateKey( 32 | modulus.valueAsBigInteger, 33 | privateExponent.valueAsBigInteger, 34 | p.valueAsBigInteger, 35 | q.valueAsBigInteger); 36 | } 37 | 38 | /// Create an [RSAPrivateKey] from the given PEM-String. 39 | static RSAPrivateKey fromPEM(String pemString) { 40 | final rows = pemString.trim().split(RegExp(r'\r\n?|\n')); 41 | final privateKeyString = rows 42 | .skipWhile((row) => row.startsWith('-----BEGIN')) 43 | .takeWhile((row) => !row.startsWith('-----END')) 44 | .map((row) => row.trim()) 45 | .join(''); 46 | return RSAPrivateKey.fromString(privateKeyString); 47 | } 48 | 49 | /// Sign an message with SHA-256 which can be verified using the associated [RSAPublicKey] 50 | @override 51 | @Deprecated('Use createSHA256Signature for creating SHA-256 signatures') 52 | String createSignature(String message) => 53 | base64.encode(createSHA256Signature(utf8.encode(message) as Uint8List)); 54 | 55 | /// Sign an message with SHA-256 which can be verified using the associated [RSAPublicKey] 56 | @override 57 | Uint8List createSHA256Signature(Uint8List message) => 58 | _createSignature(message, 'SHA-256/RSA'); 59 | 60 | /// Sign an message with SHA-512 which can be verified using the associated [RSAPublicKey] 61 | @override 62 | Uint8List createSHA512Signature(Uint8List message) => 63 | _createSignature(message, 'SHA-512/RSA'); 64 | 65 | Uint8List _createSignature(Uint8List message, String algorithm) { 66 | final signer = pointy.Signer(algorithm); 67 | pointy.AsymmetricKeyParameter privateKeyParams = 68 | pointy.PrivateKeyParameter(_privateKey); 69 | signer.init(true, privateKeyParams); 70 | final sig = signer.generateSignature(message) as pointy.RSASignature; 71 | return sig.bytes; 72 | } 73 | 74 | /// Decrypt a message which was encrypted using the associated [RSAPublicKey] 75 | String decrypt(String message) => 76 | utf8.decode(decryptData(base64.decode(message))); 77 | 78 | /// Decrypt a message which was encrypted using the associated [RSAPublicKey] 79 | Uint8List decryptData(Uint8List message) { 80 | final cipher = pointy.PKCS1Encoding(pointy.RSAEngine()); 81 | cipher.init( 82 | false, pointy.PrivateKeyParameter(_privateKey)); 83 | return _processInBlocks(cipher, message); 84 | } 85 | 86 | Uint8List _processInBlocks( 87 | pointy.AsymmetricBlockCipher engine, Uint8List input) { 88 | final numBlocks = input.length ~/ engine.inputBlockSize + 89 | ((input.length % engine.inputBlockSize != 0) ? 1 : 0); 90 | 91 | final output = Uint8List(numBlocks * engine.outputBlockSize); 92 | 93 | var inputOffset = 0; 94 | var outputOffset = 0; 95 | while (inputOffset < input.length) { 96 | final chunkSize = (inputOffset + engine.inputBlockSize <= input.length) 97 | ? engine.inputBlockSize 98 | : input.length - inputOffset; 99 | 100 | outputOffset += engine.processBlock( 101 | input, inputOffset, chunkSize, output, outputOffset); 102 | 103 | inputOffset += chunkSize; 104 | } 105 | 106 | return (output.length == outputOffset) 107 | ? output 108 | : output.sublist(0, outputOffset); 109 | } 110 | 111 | /// Get the [RSAPublicKey] of the [RSAPrivateKey] 112 | @override 113 | RSAPublicKey get publicKey => 114 | RSAPublicKey(_privateKey.modulus!, BigInt.parse('65537')); 115 | 116 | /// Export a [RSAPrivateKey] as Pointy Castle RSAPrivateKey 117 | @override 118 | pointy.RSAPrivateKey get asPointyCastle => _privateKey; 119 | 120 | /// Export a [RSAPrivateKey] as String which can be reversed using [RSAPrivateKey.fromString]. 121 | @override 122 | String toString() { 123 | final version = ASN1Integer(BigInt.from(0)); 124 | 125 | final algorithmSeq = ASN1Sequence(); 126 | final algorithmAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList( 127 | [0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1])); 128 | final paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0])); 129 | algorithmSeq.add(algorithmAsn1Obj); 130 | algorithmSeq.add(paramsAsn1Obj); 131 | 132 | final privateKeySeq = ASN1Sequence(); 133 | final modulus = ASN1Integer(_privateKey.n!); 134 | final publicExponent = ASN1Integer(BigInt.parse('65537')); 135 | final privateExponent = ASN1Integer(_privateKey.privateExponent!); 136 | final p = ASN1Integer(_privateKey.p!); 137 | final q = ASN1Integer(_privateKey.q!); 138 | final dP = _privateKey.privateExponent! % (_privateKey.p! - BigInt.from(1)); 139 | final exp1 = ASN1Integer(dP); 140 | final dQ = _privateKey.privateExponent! % (_privateKey.q! - BigInt.from(1)); 141 | final exp2 = ASN1Integer(dQ); 142 | final iQ = _privateKey.q!.modInverse(_privateKey.p!); 143 | final co = ASN1Integer(iQ); 144 | 145 | privateKeySeq.add(version); 146 | privateKeySeq.add(modulus); 147 | privateKeySeq.add(publicExponent); 148 | privateKeySeq.add(privateExponent); 149 | privateKeySeq.add(p); 150 | privateKeySeq.add(q); 151 | privateKeySeq.add(exp1); 152 | privateKeySeq.add(exp2); 153 | privateKeySeq.add(co); 154 | final publicKeySeqOctetString = 155 | ASN1OctetString(Uint8List.fromList(privateKeySeq.encodedBytes)); 156 | 157 | final topLevelSeq = ASN1Sequence(); 158 | topLevelSeq.add(version); 159 | topLevelSeq.add(algorithmSeq); 160 | topLevelSeq.add(publicKeySeqOctetString); 161 | return base64.encode(topLevelSeq.encodedBytes); 162 | } 163 | 164 | /// Export a [RSAPrivateKey] as PEM String which can be reversed using [RSAPrivateKey.fromPEM]. 165 | String toPEM() { 166 | return '-----BEGIN RSA PRIVATE KEY-----\n${toString()}\n-----END RSA PRIVATE KEY-----'; 167 | } 168 | 169 | /// Export a [RSAPrivateKey] as formatted PEM String which can be reversed using [RSAPrivateKey.fromPEM]. 170 | String toFormattedPEM() { 171 | final base = toString(); 172 | var formatted = ''; 173 | for (var i = 0; i < base.length; i++) { 174 | if (i % 64 == 0 && i != 0) { 175 | formatted += '\n'; 176 | } 177 | formatted += base[i]; 178 | } 179 | return '-----BEGIN RSA PRIVATE KEY-----\n$formatted\n-----END RSA PRIVATE KEY-----'; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /lib/src/rsa/public_key.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:asn1lib/asn1lib.dart'; 5 | import 'package:crypton/crypton.dart'; 6 | import 'package:pointycastle/export.dart' as pointy; 7 | 8 | /// [PublicKey] using RSA Algorithm 9 | class RSAPublicKey implements PublicKey { 10 | late pointy.RSAPublicKey _publicKey; 11 | 12 | /// Create an [RSAPublicKey] for the given parameters. 13 | RSAPublicKey(BigInt modulus, BigInt exponent) 14 | : _publicKey = pointy.RSAPublicKey(modulus, exponent); 15 | 16 | /// Create an [RSAPublicKey] from the given String. 17 | RSAPublicKey.fromString(String publicKeyString) { 18 | List publicKeyDER = base64Decode(publicKeyString); 19 | final asn1Parser = ASN1Parser(publicKeyDER as Uint8List); 20 | final topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; 21 | final publicKeyBitString = topLevelSeq.elements[1]; 22 | 23 | final publicKeyAsn = ASN1Parser(publicKeyBitString.contentBytes()); 24 | final publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence; 25 | final modulus = publicKeySeq.elements[0] as ASN1Integer; 26 | final exponent = publicKeySeq.elements[1] as ASN1Integer; 27 | 28 | _publicKey = pointy.RSAPublicKey( 29 | modulus.valueAsBigInteger, exponent.valueAsBigInteger); 30 | } 31 | 32 | /// Create an [RSAPublicKey] from the given PEM-String. 33 | static RSAPublicKey fromPEM(String pemString) { 34 | final rows = pemString.trim().split(RegExp(r'\r\n?|\n')); 35 | final privateKeyString = rows 36 | .skipWhile((row) => row.startsWith('-----BEGIN')) 37 | .takeWhile((row) => !row.startsWith('-----END')) 38 | .map((row) => row.trim()) 39 | .join(''); 40 | return RSAPublicKey.fromString(privateKeyString); 41 | } 42 | 43 | /// Verify the signature of a SHA256-hashed message signed with the associated [RSAPrivateKey] 44 | @Deprecated('For SHA256 signature verification use verifySHA256Signature') 45 | @override 46 | bool verifySignature(String message, String signature) => 47 | verifySHA256Signature( 48 | utf8.encode(message) as Uint8List, base64.decode(signature)); 49 | 50 | /// Verify the signature of a SHA256-hashed message signed with the associated [RSAPrivateKey] 51 | @override 52 | bool verifySHA256Signature(Uint8List message, Uint8List signature) => 53 | _verifySignature(message, signature, 'SHA-256/RSA'); 54 | 55 | /// Verify the signature of a SHA512-hashed message signed with the associated [RSAPrivateKey] 56 | @override 57 | bool verifySHA512Signature(Uint8List message, Uint8List signature) => 58 | _verifySignature(message, signature, 'SHA-512/RSA'); 59 | 60 | bool _verifySignature( 61 | Uint8List message, Uint8List signature, String algorithm) { 62 | final signer = pointy.Signer(algorithm); 63 | pointy.AsymmetricKeyParameter publicKeyParams = 64 | pointy.PublicKeyParameter(_publicKey); 65 | final sig = pointy.RSASignature(signature); 66 | signer.init(false, publicKeyParams); 67 | return signer.verifySignature(message, sig); 68 | } 69 | 70 | /// Encrypt a message which can only be decrypted using the associated [RSAPrivateKey] 71 | String encrypt(String message) => 72 | base64.encode(encryptData(utf8.encode(message) as Uint8List)); 73 | 74 | /// Encrypt a message which can only be decrypted using the associated [RSAPrivateKey] 75 | Uint8List encryptData(Uint8List message) { 76 | final cipher = pointy.PKCS1Encoding(pointy.RSAEngine()); 77 | cipher.init( 78 | true, pointy.PublicKeyParameter(_publicKey)); 79 | return cipher.process(message); 80 | } 81 | 82 | /// Export a [RSAPublicKey] as Pointy Castle RSAPublicKey 83 | @override 84 | pointy.RSAPublicKey get asPointyCastle => _publicKey; 85 | 86 | /// Export a [RSAPublic] key as String which can be reversed using [RSAPublicKey.fromString]. 87 | @override 88 | String toString() { 89 | final algorithmSeq = ASN1Sequence(); 90 | final algorithmAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList( 91 | [0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1])); 92 | final paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0])); 93 | algorithmSeq.add(algorithmAsn1Obj); 94 | algorithmSeq.add(paramsAsn1Obj); 95 | 96 | final publicKeySeq = ASN1Sequence(); 97 | publicKeySeq.add(ASN1Integer(_publicKey.modulus!)); 98 | publicKeySeq.add(ASN1Integer(_publicKey.exponent!)); 99 | final publicKeySeqBitString = 100 | ASN1BitString(Uint8List.fromList(publicKeySeq.encodedBytes)); 101 | 102 | final topLevelSeq = ASN1Sequence(); 103 | topLevelSeq.add(algorithmSeq); 104 | topLevelSeq.add(publicKeySeqBitString); 105 | return base64.encode(topLevelSeq.encodedBytes); 106 | } 107 | 108 | /// Export a [RSAPublicKey] as PEM String which can be reversed using [RSAPublicKey.fromPEM]. 109 | String toPEM() { 110 | return '-----BEGIN PUBLIC KEY-----\n${toString()}\n-----END PUBLIC KEY-----'; 111 | } 112 | 113 | /// Export a [RSAPublicKey] as formatted PEM String which can be reversed using [RSAPublicKey.fromPEM]. 114 | String toFormattedPEM() { 115 | final base = toString(); 116 | var formatted = ''; 117 | for (var i = 0; i < base.length; i++) { 118 | if (i % 64 == 0 && i != 0) { 119 | formatted += '\n'; 120 | } 121 | formatted += base[i]; 122 | } 123 | return '-----BEGIN PUBLIC KEY-----\n$formatted\n-----END PUBLIC KEY-----'; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: crypton 2 | description: A simple Dart library for asymmetric encryption and digital signatures 3 | version: 2.2.1 4 | homepage: https://github.com/konstantinullrich 5 | repository: https://github.com/konstantinullrich/crypton 6 | issue_tracker: https://github.com/konstantinullrich/crypton/issues 7 | 8 | environment: 9 | sdk: '>=2.18.0 <4.0.0' 10 | 11 | dependencies: 12 | pointycastle: ^3.7.3 13 | asn1lib: ^1.5.0 14 | 15 | dev_dependencies: 16 | lints: ^2.1.1 17 | test: ^1.24.7 18 | -------------------------------------------------------------------------------- /test/crypton_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:crypton/crypton.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | void main() { 8 | group('RSA Key Tests', () { 9 | late RSAKeypair rsaKeypair; 10 | late Uint8List message; 11 | 12 | setUp(() { 13 | rsaKeypair = RSAKeypair.fromRandom(keySize: 2048); 14 | message = utf8.encode('Crypton Test Message') as Uint8List; 15 | }); 16 | 17 | test('Private Key to String and back', () { 18 | final privateKeyString = rsaKeypair.privateKey.toString(); 19 | final privateKey = RSAPrivateKey.fromString(privateKeyString); 20 | expect(privateKey.toString(), privateKeyString); 21 | }); 22 | 23 | test('Public Key to string and back', () { 24 | final publicKeyString = rsaKeypair.publicKey.toString(); 25 | final publicKey = RSAPublicKey.fromString(publicKeyString); 26 | expect(publicKey.toString(), publicKeyString); 27 | }); 28 | 29 | test('Get Public Key from Privat Key', () { 30 | final publicKeyString = rsaKeypair.privateKey.publicKey.toString(); 31 | expect(publicKeyString, rsaKeypair.publicKey.toString()); 32 | }); 33 | 34 | test('Get Public Key from PEM-String', () { 35 | final publicKeyString = rsaKeypair.publicKey.toString(); 36 | final publicKey = RSAPublicKey.fromPEM(rsaKeypair.publicKey.toPEM()); 37 | expect(publicKey.toString(), publicKeyString); 38 | }); 39 | 40 | test('Get Private Key from PEM-String', () { 41 | final privateKeyString = rsaKeypair.privateKey.toString(); 42 | final privateKey = RSAPrivateKey.fromPEM(rsaKeypair.privateKey.toPEM()); 43 | expect(privateKey.toString(), privateKeyString); 44 | }); 45 | 46 | test('Sign and Verify deprecated', () { 47 | final signature = 48 | // ignore: deprecated_member_use_from_same_package 49 | rsaKeypair.privateKey.createSignature(utf8.decode(message)); 50 | final verified = 51 | // ignore: deprecated_member_use_from_same_package 52 | rsaKeypair.publicKey.verifySignature(utf8.decode(message), signature); 53 | expect(verified, isTrue); 54 | }); 55 | 56 | test('Sign and Verify SHA-256', () { 57 | final signature = rsaKeypair.privateKey.createSHA256Signature(message); 58 | final verified = 59 | rsaKeypair.publicKey.verifySHA256Signature(message, signature); 60 | expect(verified, isTrue); 61 | }); 62 | test('Sign and Verify SHA-512', () { 63 | final signature = rsaKeypair.privateKey.createSHA512Signature(message); 64 | final verified = 65 | rsaKeypair.publicKey.verifySHA512Signature(message, signature); 66 | expect(verified, isTrue); 67 | }); 68 | 69 | test('Encrypt and Decrypt data', () { 70 | final encrypted = rsaKeypair.publicKey.encryptData(message); 71 | final decrypted = rsaKeypair.privateKey.decryptData(encrypted); 72 | expect(decrypted, message); 73 | }); 74 | 75 | test('Encrypt and Decrypt string', () { 76 | final encrypted = rsaKeypair.publicKey.encrypt(utf8.decode(message)); 77 | final decrypted = utf8.encode(rsaKeypair.privateKey.decrypt(encrypted)); 78 | expect(decrypted, message); 79 | }); 80 | 81 | test('Private key PEM-String is formatted', () { 82 | expect( 83 | (rsaKeypair.privateKey 84 | .toFormattedPEM() 85 | .split('\n') 86 | .map((l) => l.length) 87 | .toList() 88 | ..sort()) 89 | .last, 90 | 64); 91 | }); 92 | 93 | test('Public key PEM-String is formatted', () { 94 | expect( 95 | (rsaKeypair.publicKey 96 | .toFormattedPEM() 97 | .split('\n') 98 | .map((l) => l.length) 99 | .toList() 100 | ..sort()) 101 | .last, 102 | 64); 103 | expect(rsaKeypair.publicKey.toFormattedPEM().length, 450); 104 | }); 105 | }); 106 | 107 | group('EC Key Tests', () { 108 | late ECKeypair ecKeypair; 109 | late Uint8List message; 110 | 111 | setUp(() { 112 | ecKeypair = ECKeypair.fromRandom(); 113 | message = 114 | utf8.encode(DateTime.now().millisecondsSinceEpoch.toRadixString(16)) 115 | as Uint8List; 116 | }); 117 | 118 | test('Private Key to String and back', () { 119 | final privateKeyString = ecKeypair.privateKey.toString(); 120 | final privateKey = ECPrivateKey.fromString(privateKeyString); 121 | expect(privateKey.toString(), privateKeyString); 122 | }); 123 | 124 | test('Public Key to string and back', () { 125 | final publicKeyString = ecKeypair.publicKey.toString(); 126 | final publicKey = ECPublicKey.fromString(publicKeyString); 127 | expect(publicKey.toString(), publicKeyString); 128 | }); 129 | 130 | test('Get Public Key from Privat Key', () { 131 | final publicKeyString = ecKeypair.privateKey.publicKey.toString(); 132 | expect(publicKeyString, ecKeypair.publicKey.toString()); 133 | }); 134 | 135 | test('Sign and Verify deprecated', () { 136 | final signature = 137 | // ignore: deprecated_member_use_from_same_package 138 | ecKeypair.privateKey.createSignature(utf8.decode(message)); 139 | final verified = 140 | // ignore: deprecated_member_use_from_same_package 141 | ecKeypair.publicKey.verifySignature(utf8.decode(message), signature); 142 | expect(verified, isTrue); 143 | }); 144 | 145 | test('Sign and Verify SHA-256', () { 146 | final signature = ecKeypair.privateKey.createSHA256Signature(message); 147 | final verified = 148 | ecKeypair.publicKey.verifySHA256Signature(message, signature); 149 | expect(verified, isTrue); 150 | }); 151 | 152 | test('Sign and Verify SHA-512', () { 153 | final signature = ecKeypair.privateKey.createSHA512Signature(message); 154 | final verified = 155 | ecKeypair.publicKey.verifySHA512Signature(message, signature); 156 | expect(verified, isTrue); 157 | }); 158 | }); 159 | 160 | group('KeyPair Factory Tests', () { 161 | test('Generate a random RSA Keypair', () { 162 | final keypair = RSAKeypairFactory().fromRandom(); 163 | expect(keypair, isNot(isA())); 164 | expect(keypair, isA()); 165 | }); 166 | 167 | test('Generate a random RSA Keypair asynchronously', () async { 168 | final keypair = await RSAKeypairFactory().fromRandomAsync(); 169 | expect(keypair, isNot(isA())); 170 | expect(keypair, isA()); 171 | }); 172 | 173 | test('Generate a random EC Keypair', () { 174 | final keypair = ECKeypairFactory().fromRandom(); 175 | expect(keypair, isNot(isA())); 176 | expect(keypair, isA()); 177 | }); 178 | 179 | test('Generate a random EC Keypair asynchronously', () async { 180 | final keypair = await ECKeypairFactory().fromRandomAsync(); 181 | expect(keypair, isNot(isA())); 182 | expect(keypair, isA()); 183 | }); 184 | }); 185 | 186 | group('Edge Cases', () { 187 | late RSAKeypair rsaKeypair; 188 | 189 | setUp(() { 190 | rsaKeypair = RSAKeypair.fromRandom(keySize: 2048); 191 | }); 192 | 193 | test( 194 | 'Public key PEM-String is formatted and with a leading and trailing whitespace', 195 | () { 196 | var pemLeadingWhitespace = " \n${rsaKeypair.publicKey.toFormattedPEM()}"; 197 | var pemTrailingWhitespace = "${rsaKeypair.publicKey.toFormattedPEM()}\n "; 198 | 199 | final publicKeyString = rsaKeypair.publicKey.toString(); 200 | 201 | final publicKeyLeadingWhitespace = 202 | RSAPublicKey.fromPEM(pemLeadingWhitespace); 203 | expect(publicKeyLeadingWhitespace.toString(), publicKeyString); 204 | 205 | final publicKeyTrailingWhitespace = 206 | RSAPublicKey.fromPEM(pemTrailingWhitespace); 207 | expect(publicKeyTrailingWhitespace.toString(), publicKeyString); 208 | }); 209 | 210 | test( 211 | 'Public key PEM-String is formatted and with a leading and trailing whitespace', 212 | () { 213 | var pemLeadingWhitespace = " \n${rsaKeypair.privateKey.toFormattedPEM()}"; 214 | var pemTrailingWhitespace = 215 | "${rsaKeypair.privateKey.toFormattedPEM()}\n "; 216 | 217 | final publicKeyString = rsaKeypair.privateKey.toString(); 218 | 219 | final publicKeyLeadingWhitespace = 220 | RSAPrivateKey.fromPEM(pemLeadingWhitespace); 221 | expect(publicKeyLeadingWhitespace.toString(), publicKeyString); 222 | 223 | final publicKeyTrailingWhitespace = 224 | RSAPrivateKey.fromPEM(pemTrailingWhitespace); 225 | expect(publicKeyTrailingWhitespace.toString(), publicKeyString); 226 | }); 227 | }); 228 | } 229 | --------------------------------------------------------------------------------