How to generate a private/public key pair to encrypt messages

12,862

Solution 1

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

Solution 2

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

Solution 3

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-----""";
    }
}
Share:
12,862

Related videos on Youtube

Shamshun
Author by

Shamshun

Updated on September 15, 2022

Comments

  • Shamshun
    Shamshun over 1 year

    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?

  • Shamshun
    Shamshun over 5 years
    Unfortunately it looks like PointyCastle doesn't work with Dart2 at the moment. Unit tests fail to generate an RSA keypair (using RSAKeyGenerator). (Issue: github.com/PointyCastle/pointycastle/pull/122)
  • Günter Zöchbauer
    Günter Zöchbauer over 5 years
    The pre-release version 1.0.0-rc4 can be used with Flutter. Only the latest released version 0.11.1 is not Dart 2 compatible and can't be used with Flutter. The DART 2 INCOMPATIBLE banner only applies to the latest released version.
  • Shamshun
    Shamshun over 5 years
    That's true. The package is not marked DART 2 INCOMPATIBLE. I'm using 1.0.0-rc4, I've been trying for two weeks to solve errors, but it's not working. Please take a look at the link I provided. There are issues with 1.0.0-rc4 still. (github.com/PointyCastle/pointycastle/issues/…)
  • Shamshun
    Shamshun over 5 years
    Update: I somehow managed to generate a keypair and encrypt/decrypt a message using pointycastles in Dart2. To do that, follow this thread closely: github.com/PointyCastle/pointycastle/issues/127 When faced with errors/exceptions, try and use code from this proteye's git (RsaKeyHelper Class) : gist.github.com/proteye/982d9991922276ccfb011dfc55443d74 Warning: Some very annoying bugs ahead.
  • Randika Vishman
    Randika Vishman over 4 years
    Do you guys still use Pointy Castle library for cryptography on Flutter?
  • Alberto M
    Alberto M over 2 years
    unfortunately the code for the conversion to PEM doesn't work anymore