Skip to content

Instantly share code, notes, and snippets.

@evaisse
Created March 11, 2025 09:12
Show Gist options
  • Save evaisse/141c8a2b5b1c4fa7c74a300f6f7deeab to your computer and use it in GitHub Desktop.
Save evaisse/141c8a2b5b1c4fa7c74a300f6f7deeab to your computer and use it in GitHub Desktop.
Asymmetric Encryption RSA-AES-CBC with public/private keys using pointycastle lib
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