import 'dart:convert'; import 'dart:math'; import 'dart:typed_data'; import 'package:pointycastle/export.dart'; import 'package:asn1lib/asn1lib.dart'; /* var rsaKeyHelper = RsaKeyHelper(); var keyPair = rsaKeyHelper.generateRSAkeyPair(); var rsaPub = keyPair.publicKey; var rsaPriv = keyPair.privateKey; var cipherText = rsaKeyHelper.rsaEncrypt(rsaPub, Uint8List.fromList('Test Data'.codeUnits)); print(cipherText); var plainText = rsaKeyHelper.rsaDecrypt(rsaPriv, cipherText); print(String.fromCharCodes(plainText)); */ List decodePEM(String pem) { var startsWith = [ '-----BEGIN PUBLIC KEY-----', '-----BEGIN PUBLIC KEY-----', '\n-----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.contains('BEGIN PGP'); 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 { // Generate secure random sequence for seed SecureRandom secureRandom() { var secureRandom = FortunaRandom(); var random = Random.secure(); List seeds = []; for (int i = 0; i < 32; i++) { seeds.add(random.nextInt(255)); } secureRandom.seed(KeyParameter(Uint8List.fromList(seeds))); return secureRandom; } // Generate RSA key pair AsymmetricKeyPair generateRSAkeyPair({int bitLength = 2048}) { var secureRandom = this.secureRandom(); // final keyGen = KeyGenerator('RSA'); // Get using registry final keyGen = RSAKeyGenerator(); keyGen.init(ParametersWithRandom( RSAKeyGeneratorParameters(BigInt.parse('65537'), bitLength, 64), secureRandom)); // Use the generator final pair = keyGen.generateKeyPair(); // Cast the generated key pair into the RSA key types final myPublic = pair.publicKey as RSAPublicKey; final myPrivate = pair.privateKey as RSAPrivateKey; return AsymmetricKeyPair(myPublic, myPrivate); } // Encrypt data using RSA key Uint8List rsaEncrypt(RSAPublicKey myPublic, Uint8List dataToEncrypt) { final encryptor = OAEPEncoding(RSAEngine()) ..init(true, PublicKeyParameter(myPublic)); // true=encrypt return _processInBlocks(encryptor, dataToEncrypt); } // Decrypt data using RSA key Uint8List rsaDecrypt(RSAPrivateKey myPrivate, Uint8List cipherText) { final decryptor = OAEPEncoding(RSAEngine()) ..init(false, PrivateKeyParameter(myPrivate)); // false=decrypt return _processInBlocks(decryptor, cipherText); } // Process blocks after encryption/descryption Uint8List _processInBlocks(AsymmetricBlockCipher engine, Uint8List input) { final numBlocks = input.length ~/ engine.inputBlockSize + ((input.length % engine.inputBlockSize != 0) ? 1 : 0); final output = Uint8List(numBlocks * engine.outputBlockSize); var inputOffset = 0; var outputOffset = 0; while (inputOffset < input.length) { final chunkSize = (inputOffset + engine.inputBlockSize <= input.length) ? engine.inputBlockSize : input.length - inputOffset; outputOffset += engine.processBlock( input, inputOffset, chunkSize, output, outputOffset); inputOffset += chunkSize; } return (output.length == outputOffset) ? output : output.sublist(0, outputOffset); } // Encode RSA public key to pem format static encodePublicKeyToPem(RSAPublicKey publicKey) { var algorithmSeq = ASN1Sequence(); var algorithmAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1])); var paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0])); algorithmSeq.add(algorithmAsn1Obj); algorithmSeq.add(paramsAsn1Obj); var publicKeySeq = ASN1Sequence(); publicKeySeq.add(ASN1Integer(publicKey.modulus!)); publicKeySeq.add(ASN1Integer(publicKey.exponent!)); var publicKeySeqBitString = ASN1BitString(Uint8List.fromList(publicKeySeq.encodedBytes)); var topLevelSeq = 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-----"""; } // Parse public key PEM file static parsePublicKeyFromPem(pemString) { List publicKeyDER = decodePEM(pemString); var asn1Parser = ASN1Parser(Uint8List.fromList(publicKeyDER)); var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; var publicKeyBitString = topLevelSeq.elements[1]; var publicKeyAsn = ASN1Parser(publicKeyBitString.contentBytes()!, relaxedParsing: true); var publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence; var modulus = publicKeySeq.elements[0] as ASN1Integer; var exponent = publicKeySeq.elements[1] as ASN1Integer; RSAPublicKey rsaPublicKey = RSAPublicKey( modulus.valueAsBigInteger!, exponent.valueAsBigInteger! ); return rsaPublicKey; } // Encode RSA private key to pem format static encodePrivateKeyToPem(RSAPrivateKey privateKey) { var version = ASN1Integer(BigInt.zero); var algorithmSeq = ASN1Sequence(); var algorithmAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([ 0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1 ])); var paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0])); algorithmSeq.add(algorithmAsn1Obj); algorithmSeq.add(paramsAsn1Obj); var privateKeySeq = ASN1Sequence(); var modulus = ASN1Integer(privateKey.n!); var publicExponent = ASN1Integer(BigInt.parse('65537')); var privateExponent = ASN1Integer(privateKey.privateExponent!); var p = ASN1Integer(privateKey.p!); var q = ASN1Integer(privateKey.q!); var dP = privateKey.privateExponent! % (privateKey.p! - BigInt.one); var exp1 = ASN1Integer(dP); var dQ = privateKey.privateExponent! % (privateKey.q! - BigInt.one); 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 = ASN1OctetString(Uint8List.fromList(privateKeySeq.encodedBytes)); var topLevelSeq = 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-----"""; } static parsePrivateKeyFromPem(pemString) { List privateKeyDER = decodePEM(pemString); var asn1Parser = ASN1Parser(Uint8List.fromList(privateKeyDER)); var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence; var version = topLevelSeq.elements[0]; var algorithm = topLevelSeq.elements[1]; var privateKey = topLevelSeq.elements[2]; asn1Parser = 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; } }