Created
March 11, 2025 09:12
-
-
Save evaisse/141c8a2b5b1c4fa7c74a300f6f7deeab to your computer and use it in GitHub Desktop.
Asymmetric Encryption RSA-AES-CBC with public/private keys using pointycastle lib
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:convert'; | |
import 'dart:math'; | |
import 'dart:typed_data'; | |
import 'package:basic_utils/basic_utils.dart'; | |
import 'package:encrypt/encrypt.dart'; | |
// ignore: depend_on_referenced_packages | |
import 'package:crypto/crypto.dart'; | |
import 'package:flutter/foundation.dart' as flutter; | |
import 'package:pointycastle/export.dart'; | |
typedef EncryptionResult = ({ | |
Uint8List aesEncryptedPayload, | |
Uint8List aesKey, | |
Uint8List aesIV, | |
String? keyChecksum, | |
}); | |
class AsymmetricEncryption { | |
final _RsaAesCbcEncoder _encoder; | |
final AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> _keys; | |
final String publicKeyAsPem; | |
final String privateKeyAsPem; | |
factory AsymmetricEncryption.rsa() { | |
final encoder = _RsaAesCbcEncoder(); | |
final keys = encoder.generateRSAKeyPair(); | |
final pem = CryptoUtils.encodeRSAPublicKeyToPem(keys.publicKey); | |
final privatePem = CryptoUtils.encodeRSAPrivateKeyToPem(keys.privateKey); | |
return AsymmetricEncryption( | |
keys: keys, | |
encoder: encoder, | |
publicKeyAsPem: pem, | |
privateKeyAsPem: privatePem, | |
); | |
} | |
AsymmetricEncryption({ | |
required _RsaAesCbcEncoder encoder, | |
required AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> keys, | |
required this.publicKeyAsPem, | |
required this.privateKeyAsPem, | |
}) : _encoder = encoder, | |
_keys = keys; | |
String encrypt(String data) { | |
final req = { | |
"encrypt": "aes-cbc-256", | |
"publicKey": base64Encode(utf8.encode(publicKeyAsPem)), | |
"data": base64Encode(utf8.encode(data)), | |
}; | |
return base64Encode(utf8.encode(jsonEncode(req))); | |
} | |
String decrypt(String token) { | |
final json = utf8.decode(base64Decode(token)); | |
final res = jsonDecode(json); | |
if (res | |
case ({ | |
'publicKeyChecksum': String publicKeyPemCheckSum, | |
'key': String aesKey, | |
'iv': String aesIV, | |
'data': String aesEncryptedPayload, | |
})) { | |
if (publicKeyPemCheckSum != "SHA256:${sha256.convert(utf8.encode(publicKeyAsPem))}") { | |
throw 'invalid pubic key checksum'; | |
} | |
return _encoder.decryptAesCbc( | |
( | |
aesKey: base64.decode(aesKey), | |
aesEncryptedPayload: base64.decode(aesEncryptedPayload), | |
aesIV: base64.decode(aesIV), | |
keyChecksum: publicKeyPemCheckSum, | |
), | |
_keys, | |
); | |
} | |
throw "invalid json input"; | |
} | |
} | |
class _RsaAesCbcEncoder { | |
Uint8List decryptRsa(Uint8List data, AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> pair) { | |
final algo = RSA( | |
publicKey: pair.publicKey, | |
privateKey: pair.privateKey, | |
encoding: RSAEncoding.OAEP, | |
); | |
final encrypter = Encrypter(algo); | |
final res = encrypter.decryptBytes(Encrypted(data)); | |
assert(res.length == 32); | |
return Uint8List.fromList(res); | |
} | |
String decryptAesCbc(EncryptionResult res, AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> pair) { | |
final aesKey = decryptRsa(res.aesKey, pair); | |
final encrypter = Encrypter(AES(Key(aesKey), mode: AESMode.cbc)); | |
return encrypter.decrypt(Encrypted(res.aesEncryptedPayload), iv: IV(res.aesIV)); | |
} | |
AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> generateRSAKeyPair() { | |
if (flutter.kIsWeb) { | |
return AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey>( | |
CryptoUtils.rsaPublicKeyFromPemPkcs1(_publicKeyPem), | |
CryptoUtils.rsaPrivateKeyFromPemPkcs1(_privateKeyPem), | |
); | |
} | |
final randomSeed = Uint8List.fromList(List<int>.generate(32, (i) => i)); | |
final rsaKeyGenerator = RSAKeyGeneratorParameters(BigInt.from(flutter.kIsWeb ? 15 : 65537), 4096, 64); | |
final randomGenerator = FortunaRandom()..seed(KeyParameter(randomSeed)); | |
final keyGen = RSAKeyGenerator() | |
..init( | |
ParametersWithRandom( | |
rsaKeyGenerator, | |
randomGenerator, | |
), | |
); | |
final pair = keyGen.generateKeyPair(); | |
final myPublic = pair.publicKey as RSAPublicKey; | |
final myPrivate = pair.privateKey as RSAPrivateKey; | |
return AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey>(myPublic, myPrivate); | |
} | |
String generatebase64RandomKey({int length = 32}) { | |
final random = Random.secure(); | |
final items = List<int>.generate(length, (i) => random.nextInt(256)); | |
return base64.encode(items); | |
} | |
} | |
final _privateKeyPem = '''-----BEGIN RSA PRIVATE KEY----- | |
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 | |
-----END RSA PRIVATE KEY-----'''; | |
final _publicKeyPem = '''-----BEGIN RSA PUBLIC KEY----- | |
MIICCgKCAgEAq1uKX3ll4G6iU8vBSEEl7/2oBV4bKh0uCrjLEd7y755TDgZ6+k/HlECT3/nBSvQLj2JAczm52Blsx3TtMtgBs6lAZqCC41QAgrHw3hvMWFGCJF3EuIHRqGnMopwxhQ/P7gmTIPCj5Ze70Mp3+oA8gF+wuN6dII2ux1RYwB65vJ3TYuPlGJ85CdUbUwX5k1P+LYfIAg1ITPnLMdX2VyCubhfep7rGb6W/7ADFWa81WOA28wcgdu29ekRKI2cQxZlW/PbcodeocMlpfHO4/cDQWHUYgez19Yp3WkOvAwZHC3Aeyk5NID+ACwdCW2uDKm0AgyZK1uVL78SUqdNcmaJvraoTMT4rAYmCTZoYDnJFB2ZHrVwUHF2KPT63LtUL1gd0NybqHRO0DJMqyQ7qFOZnvla26Izrpzm532WMVWPN/SCGXl4544MM/UoLmJhdBHMZbmI7/LafDp5Jhg3xdNwEnpy7isOVOX8XYnmcDKmvjzB0xd6n/mME+UZHreqbRk8yuLvQFUuoWCNB1MhDsjcrFPgGzqc4RQ7Z6cDG+1w5JEROMH6IMwF0UbQMIsTfxLtQH8utf0Z/bodi67I/XmrpujNDyRg8SXVZR0QrfNhEpNTPjbINCmrRUdkU3IAtru6hgd/Ng16mVTIH8GKl3s8TxissEXyegQGRjFtSx+6RiCsCAwEAAQ== | |
-----END RSA PUBLIC KEY-----'''; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment