I need to generate a keypair in my flutter App, but it seems that there aren't any libraries to do so. There is one library called RSA which does parse a pair of public/private keys and is able to encrypt and decrypt strings using them, but it doesn't have the ability to generate a new KeyPair (preferably from a given string).
How can I generate the keys in the first place? Am I missing something?
There is a Dart2 and Flutter compatible pre-release of Pointycastle available.
The default README.md points to the first non-pre-release version and therefore the frontpage shows "DART 2 INCOMPATIBLE", but that only applies to version < 0.11.1
.
Just add to pubspec.yaml
dependencies:
pointycastle: ^1.0.0-rc4
For examples check the unit tests https://github.com/PointyCastle/pointycastle/blob/master/test/key_generators/rsa_key_generator_test.dart
I started using some sample code for generating public key / private key pair using pointy castles. After a long time of debugging and editing code, I ended up with the following code:
var keyParams = new RSAKeyGeneratorParameters(BigInt.from(65537), 2048, 5);
var secureRandom = new FortunaRandom();
var random = new Random.secure();
List<int> seeds = [];
for (int i = 0; i < 32; i++) {
seeds.add(random.nextInt(255));
}
secureRandom.seed(new KeyParameter(new Uint8List.fromList(seeds)));
var rngParams = new ParametersWithRandom(keyParams, secureRandom);
var k = new RSAKeyGenerator();
k.init(rngParams);
var keyPair = k.generateKeyPair();
print(new RsaKeyHelper().encodePublicKeyToPem(keyPair.publicKey) );
print(new RsaKeyHelper().encodePrivateKeyToPem(keyPair.privateKey) );
AsymmetricKeyParameter<RSAPublicKey> keyParametersPublic = new PublicKeyParameter(keyPair.publicKey);
var cipher = new RSAEngine()..init(true, keyParametersPublic);
var cipherText = cipher.process(new Uint8List.fromList("Hello World".codeUnits));
print("Encrypted: ${new String.fromCharCodes(cipherText)}");
AsymmetricKeyParameter<RSAPrivateKey> keyParametersPrivate = new PrivateKeyParameter(keyPair.privateKey);
cipher.init( false, keyParametersPrivate )
;
var decrypted = cipher.process(cipherText);
print("Decrypted: ${new String.fromCharCodes(decrypted)}");
I used this guy's edits from this git issue page (Look here for some more functions such as encoding to / decoding from PEM
you can use with this structure:
import 'rsa_helper.dart';
final keyPair = RsaKeyHelper().generateKeyPair();
if you want encrypt text:
String keyBase64Encrypted = RsaKeyHelper().encrypt('Hello', keyPair);
if you want decrypt, before you must convert data to base64 like above encrypt
function, and after that you can use :
String keyPlainText = RsaKeyHelper().decrypt(keyBase64Encrypted, keyPair);
you need this file fixe_secure_random.dart
and I use encrypt pub spec. in pubspec.yaml
:
dependencies:
encrypt: ^3.3.1
install. I change the rsa_helper.dart
to below. you can create a new file and past code to it and use it. also you can get export pem file.
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:encrypt/encrypt.dart';
import "package:pointycastle/export.dart";
import "package:asn1lib/asn1lib.dart";
import 'fixed_secure_random.dart';
List<int> decodePEM(String pem) {
var startsWith = [
"-----BEGIN PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----",
"-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: React-Native-OpenPGP.js 0.1\r\nComment: http://openpgpjs.org\r\n\r\n",
"-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: React-Native-OpenPGP.js 0.1\r\nComment: http://openpgpjs.org\r\n\r\n",
];
var endsWith = [
"-----END PUBLIC KEY-----",
"-----END PRIVATE KEY-----",
"-----END PGP PUBLIC KEY BLOCK-----",
"-----END PGP PRIVATE KEY BLOCK-----",
];
bool isOpenPgp = pem.indexOf('BEGIN PGP') != -1;
for (var s in startsWith) {
if (pem.startsWith(s)) {
pem = pem.substring(s.length);
}
}
for (var s in endsWith) {
if (pem.endsWith(s)) {
pem = pem.substring(0, pem.length - s.length);
}
}
if (isOpenPgp) {
var index = pem.indexOf('\r\n');
pem = pem.substring(0, index);
}
pem = pem.replaceAll('\n', '');
pem = pem.replaceAll('\r', '');
return base64.decode(pem);
}
class RsaKeyHelper {
AsymmetricKeyPair<PublicKey, PrivateKey> generateKeyPair() {
// var keyParams = new RSAKeyGeneratorParameters(BigInt.parse('65537'), 2048, 12);
//
// var secureRandom = new FortunaRandom();
// var random = new Random.secure();
// List<int> seeds = [];
// for (int i = 0; i < 32; i++) {
// seeds.add(random.nextInt(255));
// }
// secureRandom.seed(new KeyParameter(new Uint8List.fromList(seeds)));
//
// var rngParams = new ParametersWithRandom(keyParams, secureRandom);
// var k = new RSAKeyGenerator();
// k.init(rngParams);
//
// return k.generateKeyPair();
var rnd = new FixedSecureRandom();
var rsapars = new RSAKeyGeneratorParameters(BigInt.parse("65537"), 2048, 12);
var params = new ParametersWithRandom(rsapars, rnd);
var keyGenerator = new KeyGenerator("RSA");
keyGenerator.init(params);
return keyGenerator.generateKeyPair();
// final n = "24649663692047164444790643172109370056158709234977203368650147515375245495213442567159484352023028564722607846088040100966055452012530635310929880142309672672370384513414361688667706499439717428347689592753696423610988570895714214920908622106527744596538403468957028226105712420419053355165486523922578029360613666994642331140679324765028868432884033287641095549662040120859273059357594690379309402039994712237709233598606723986537440109010028591440539106473660495784943265016397899779881916920074735104005566018575893835392960697074083820748729243932835684709199184189144330411532000389215869317902155568589589990937";
// final e = "65537";
// final d = "4063958011391574405693927086602098714570316817735457564402777727140844524097551717932743769528797833923246071333464657993778005691371187490037648274068938358865404346665886110838985133992189859366415704864484029740707257099473459148578934981171434157279055334880764911165787611618289996529640995025458223709324848295784182981653521435775135990468151575617415377402359600649196585978093468870169020045627061059125356549455475504958158218546129405411183104108490830376537830617352916950230417099192711318868054030047466626509446719907850666657436082569747996909170379147599626458818599142452671385912424304169295269813";
// final p = "172622988945032241272460594823465727338165469761128582817141296878210996104803340949368540074325243384642718754688224642044903791277270607713426667425998282894170126560269953218957312807290242694167112152302773291330789947866948729188625784460892934126377883318018638038433339229978409110366812585246469634979";
// final q = "142794791369857893518216846152178512365742893193381905408671045383524196038880042688222484665321416966726996826180091369625084659347039997384427839884481875048370565094771341455209579084576003589416828973425288956690619644847967193297411322186095969144940769802524422661706293551748267739368517289465924234003";
// final publicKey = new RSAPublicKey(BigInt.parse(n), BigInt.parse(e));
// final privKey = new RSAPrivateKey(BigInt.parse(n), BigInt.parse(d), BigInt.parse(p), BigInt.parse(q));
// return new AsymmetricKeyPair(
// publicKey,
// privKey,);
}
String encrypt(String plaintext, AsymmetricKeyPair<PublicKey, PrivateKey> keyPair) {
final encrypter = Encrypter(RSA(publicKey: keyPair.publicKey, privateKey: keyPair.privateKey));
return encrypter.encrypt(plaintext).base64;
}
String decrypt(String cipherBase64, AsymmetricKeyPair<PublicKey, PrivateKey> keyPair) {
final encrypter = Encrypter(RSA(publicKey: keyPair.publicKey, privateKey: keyPair.privateKey));
return encrypter.decrypt64(cipherBase64);
}
parsePublicKeyFromPem(pemString) {
List<int> publicKeyDER = decodePEM(pemString);
var asn1Parser = new ASN1Parser(publicKeyDER);
var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
var publicKeyBitString = topLevelSeq.elements[1];
var publicKeyAsn = new ASN1Parser(publicKeyBitString.contentBytes());
ASN1Sequence publicKeySeq = publicKeyAsn.nextObject();
var modulus = publicKeySeq.elements[0] as ASN1Integer;
var exponent = publicKeySeq.elements[1] as ASN1Integer;
RSAPublicKey rsaPublicKey = RSAPublicKey(
modulus.valueAsBigInteger,
exponent.valueAsBigInteger
);
return rsaPublicKey;
}
parsePrivateKeyFromPem(pemString) {
List<int> privateKeyDER = decodePEM(pemString);
var asn1Parser = new ASN1Parser(privateKeyDER);
var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
var version = topLevelSeq.elements[0];
var algorithm = topLevelSeq.elements[1];
var privateKey = topLevelSeq.elements[2];
asn1Parser = new ASN1Parser(privateKey.contentBytes());
var pkSeq = asn1Parser.nextObject() as ASN1Sequence;
version = pkSeq.elements[0];
var modulus = pkSeq.elements[1] as ASN1Integer;
var publicExponent = pkSeq.elements[2] as ASN1Integer;
var privateExponent = pkSeq.elements[3] as ASN1Integer;
var p = pkSeq.elements[4] as ASN1Integer;
var q = pkSeq.elements[5] as ASN1Integer;
var exp1 = pkSeq.elements[6] as ASN1Integer;
var exp2 = pkSeq.elements[7] as ASN1Integer;
var co = pkSeq.elements[8] as ASN1Integer;
RSAPrivateKey rsaPrivateKey = RSAPrivateKey(
modulus.valueAsBigInteger,
privateExponent.valueAsBigInteger,
p.valueAsBigInteger,
q.valueAsBigInteger
);
return rsaPrivateKey;
}
encodePublicKeyToPem(RSAPublicKey publicKey) {
var algorithmSeq = new ASN1Sequence();
var algorithmAsn1Obj = new ASN1Object.fromBytes(Uint8List.fromList([0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1]));
var paramsAsn1Obj = new ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
algorithmSeq.add(algorithmAsn1Obj);
algorithmSeq.add(paramsAsn1Obj);
var publicKeySeq = new ASN1Sequence();
publicKeySeq.add(ASN1Integer(publicKey.modulus));
publicKeySeq.add(ASN1Integer(publicKey.exponent));
var publicKeySeqBitString = new ASN1BitString(Uint8List.fromList(publicKeySeq.encodedBytes));
var topLevelSeq = new ASN1Sequence();
topLevelSeq.add(algorithmSeq);
topLevelSeq.add(publicKeySeqBitString);
var dataBase64 = base64.encode(topLevelSeq.encodedBytes);
return """-----BEGIN PUBLIC KEY-----\r\n$dataBase64\r\n-----END PUBLIC KEY-----""";
}
encodePrivateKeyToPem(RSAPrivateKey privateKey) {
var version = ASN1Integer(BigInt.from(0));
var algorithmSeq = new ASN1Sequence();
var algorithmAsn1Obj = new ASN1Object.fromBytes(Uint8List.fromList([0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1]));
var paramsAsn1Obj = new ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
algorithmSeq.add(algorithmAsn1Obj);
algorithmSeq.add(paramsAsn1Obj);
var privateKeySeq = new ASN1Sequence();
var modulus = ASN1Integer(privateKey.n);
var publicExponent = ASN1Integer(BigInt.parse('65537'));
var privateExponent = ASN1Integer(privateKey.d);
var p = ASN1Integer(privateKey.p);
var q = ASN1Integer(privateKey.q);
var dP = privateKey.d % (privateKey.p - BigInt.from(1));
var exp1 = ASN1Integer(dP);
var dQ = privateKey.d % (privateKey.q - BigInt.from(1));
var exp2 = ASN1Integer(dQ);
var iQ = privateKey.q.modInverse(privateKey.p);
var co = ASN1Integer(iQ);
privateKeySeq.add(version);
privateKeySeq.add(modulus);
privateKeySeq.add(publicExponent);
privateKeySeq.add(privateExponent);
privateKeySeq.add(p);
privateKeySeq.add(q);
privateKeySeq.add(exp1);
privateKeySeq.add(exp2);
privateKeySeq.add(co);
var publicKeySeqOctetString = new ASN1OctetString(Uint8List.fromList(privateKeySeq.encodedBytes));
var topLevelSeq = new ASN1Sequence();
topLevelSeq.add(version);
topLevelSeq.add(algorithmSeq);
topLevelSeq.add(publicKeySeqOctetString);
var dataBase64 = base64.encode(topLevelSeq.encodedBytes);
return """-----BEGIN PRIVATE KEY-----\r\n$dataBase64\r\n-----END PRIVATE KEY-----""";
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With