how to do rsa encryption using publicKey.xml file in flutter dart , i am getting issue FormatException: Could not parse BigInt

479 Views Asked by At
<RSAKeyValue><Modulus>D4ZblrI8NsZgnZOQbHJuEv7Lg0pCOuhwmGIS10rwlJ9Z/hGbes=</Modulus><Exponent>ABCD</Exponent><P>86TWhp553PrrlQ/xdOpGDbGy8/h3/5vg+P==</P><Q>D33zQmGtBnoO8OaQhCwbwqai1w==</Q><DP>i0RwPcLPh+WQKt5L75SR1PQ==</DP><DQ>3vayrPOJ1WMFxPzCpqMaUDWwWsBJ/c0PRwRhvrSw4/TR+6BlEkU/5b17/==</DQ><InverseQ>CbuGb8ZNvjCkSHqvSTJvFpFfqxxQjH09ABxBZx3K7SNvw3+6+LfKeRzoz7==</InverseQ><D>p6etDZ1ghROHdHWAauTUe6zn/RQzm7HE5aCWVOgMqcg9VkFMMZ3H+R1rec=</D></RSAKeyValue>

above you can see the sample format of my publicKey.xml file , i need to convert this into pem format for rsa encryption , mainly need to encrypt the username & password.

import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:xml/xml.dart' as xml;

void encryptRSa() {
  String publicKeyXML = '''
   <RSAKeyValue><Modulus>D4ZblrI8NsZgnZOQbHJuEv7Lg0pCOuhwmGIS10rwlJ9Z/hGbes=</Modulus><Exponent>ABCD</Exponent><P>86TWhp553PrrlQ/xdOpGDbGy8/h3/5vg+P==</P><Q>D33zQmGtBnoO8OaQhCwbwqai1w==</Q><DP>i0RwPcLPh+WQKt5L75SR1PQ==</DP><DQ>3vayrPOJ1WMFxPzCpqMaUDWwWsBJ/c0PRwRhvrSw4/TR+6BlEkU/5b17/==</DQ><InverseQ>CbuGb8ZNvjCkSHqvSTJvFpFfqxxQjH09ABxBZx3K7SNvw3+6+LfKeRzoz7==</InverseQ><D>p6etDZ1ghROHdHWAauTUe6zn/RQzm7HE5aCWVOgMqcg9VkFMMZ3H+R1rec=</D></RSAKeyValue>
  ''';

  var document = xml.XmlDocument.parse(publicKeyXML);
  var modulusBase64 = document.findAllElements('Modulus').single.text;
  var exponentBase64 = document.findAllElements('Exponent').single.text;

  var modulusBytes = Uint8List.fromList(base64.decode(modulusBase64));
  var exponentBytes = Uint8List.fromList(base64.decode(exponentBase64));

  var modulus = decodeBigIntFromBytes(modulusBytes);
  var exponent = decodeBigIntFromBytes(exponentBytes);

  var publicKey = RSAPublicKey(modulus, exponent);

  var encryptor = OAEPEncoding(RSAEngine())
    ..init(true, PublicKeyParameter<RSAPublicKey>(publicKey));

  String message = 'Hello, World!';

  var encrypted = encryptor.process(Uint8List.fromList(utf8.encode(message)));

  print('EncryptedOne: ${base64.encode(encrypted)}');
}

BigInt decodeBigIntFromBytes(Uint8List bytes) {
  return BigInt.parse(base64.encode(bytes), radix: 16);
}

i am using pointcastle & xml library for encryption , when i run this code i am getting the issue FormatException: Could not parse BigInt

1

There are 1 best solutions below

11
Topaco On BEST ANSWER

The bug is that in the function decodeBigIntFromBytes() the data in BigInt.parse() is incorrectly Base64 encoded. Correct is a hex encoding, corresponding to the radix 16 used by you:

import 'package:convert/convert.dart';
...
BigInt decodeBigIntFromBytes(Uint8List bytes) {
  return BigInt.parse(hex.encode(bytes), radix: 16);
}

With this change, the code works, both the import of the public key and the encryption itself.


Additional, but minor: Instead of the expired text, innerText should be used, e.g:

var modulusBase64 = document.findAllElements('Modulus').single.innerText;

If you want to export/import the key in PEM format, the basic_utils package provides suitable functions, e.g. CryptoUtils.encodeRSAPublicKeyToPem() can be used to export the public key PEM encoded in X.509/SPKI format:

import 'package:basic_utils/basic_utils.dart';
...
print('X.509/SPKI, PEM: ${CryptoUtils.encodeRSAPublicKeyToPem(publicKey)}');

Note that the posted key, as already noted in the comments, is indeed a private RSA key. A public key only contains Modulus and Exponent field (all other fields belong to the private key and must not be disclosed). However, since this is obviously a test key, disclosure is probably not critical.


Complete code with valid test key:

import 'dart:convert';
import 'dart:typed_data';
import 'package:convert/convert.dart';
import 'package:pointycastle/export.dart';
import 'package:xml/xml.dart' as xml;
import 'package:basic_utils/basic_utils.dart';

...

void encryptRSa() {

  // key import
  var publicKeyXML = '''<RSAKeyValue><Modulus>unF5aDa6HCfLMMI/MZLT5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1EbYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQwKtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1xH9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4iGw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>''';
  var xmlDocument = xml.XmlDocument.parse(publicKeyXML);
  var modulus = getData(xmlDocument, 'Modulus');
  var exponent = getData(xmlDocument, 'Exponent');
  var publicKey = RSAPublicKey(modulus, exponent);

  // encryption
  var message = 'Hello, World!';
  var encryptor = OAEPEncoding(RSAEngine())..init(true, PublicKeyParameter<RSAPublicKey>(publicKey)); // Applies SHA-1 by default for both digests, i.e. the OAEP and MGF1 digest, and an empty label
  var encrypted = encryptor.process(Uint8List.fromList(utf8.encode(message)));
  print('EncryptedOne: ${base64.encode(encrypted)}');

  // key export
  print('X.509/SPKI, PEM: ${CryptoUtils.encodeRSAPublicKeyToPem(publicKey)}');
}

BigInt getData(xml.XmlDocument xmlDocument, String element){
  var dataB64 = xmlDocument.findAllElements(element).single.innerText;
  var dataBytes = Uint8List.fromList(base64.decode(dataB64));
  return BigInt.parse(hex.encode(dataBytes), radix: 16);
}

Edit: Regarding the question from the comment about importing the private key. The import of the private key is analogous. The elements Modulus, D (private exponent), P (prime 1) and Q (prime 2) are required. These values are used to instantiate RSAPrivateKey, which can then be applied for decryption.

Complete code with valid private key (associated with the public key from the encryption code):

...
// key import
var privateKeyXML = '''<RSAKeyValue><Modulus>unF5aDa6HCfLMMI/MZLT5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1EbYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQwKtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1xH9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4iGw==</Modulus><Exponent>AQAB</Exponent><P>3e+jND6OS6ofGYUN6G4RapHzuRAV8ux1C9eXMOdZFbcBehn/ydhzR48LIPTW9HiRE00um27lXfW5/POCaEUvfOp1UxTWeHZ4xICo40PBo383ZKW1MbES1oiMbjkEqSFGRnTItnLU07bKbzLA7I0UWHWCEAnv0g7HRxk973FAsm8=</P><Q>1w8+olZ2POBYeYgw1a0DkeJWKMQi/4pAgyYwustZo0dHlRXQT0OI9XQ0j1PZWoQS28tFcmoEAg6f5MUDpdM9swS0SOCPI1Lc/f/Slus3u1O3UCezk37pneSPezskDhvV2cClJEYH8m/zwDAUlEi4KLIt/H/jgtyDd6pbxxc78RU=</Q><DP>iE6VAxJknM4oeakBiL6JTdXEReY+RMu7e4F2518/lJmoe5CaTCL3cnzFTgFyQAYIvD0MIgSzNMkl6Ni6QEY1y1fIpTVIIAZLWAzZLXPA6yTIJbWsmo9xzXdiIJQ+a433NnClkYDne/xpSnB2kxJ263mIX0drFq1i8STsqDH7lVs=</DP><DQ>VqUJsxXqpTQt8Sjxo+UE3y21UM9U2me0/iHQ2DE9eA8rw+D6ADVRZLLgyi4aD+HOR0dqP2J/IuUJfn3xrkmhPhLTH9l5Ud38s0jya2NxHMPpwx17uB0Vuktvk1KMgDKuwgBfiHG+meqI5hF4+RUjPSIsbOKJoxt8zCWSvG+b8tE=</DQ><InverseQ>s9Fu1JsTak+C84codMY+vuApuaxZVs5xADysbzTVPfxb9Q97Ve3KcwSPPNDb05pV5DC9Q334PEVcnpi/CPqKHhZ2rXT2Ls6jV8OcxzM5A30MpyHZ40Aes1I4zIsMIGb77BvIcCxLZPRU7z6DMsAG+JmbkAUJBZ+R7gtmjmY5LXQ=</InverseQ><D>SlJj0ExIomKmmBhG8q8SM1s2sWG6gdQMjs6MEeluRT/1c2v79cq2Dum5y/+UBl8x8TUKPKSLpCLs+GXkiVKgHXrFlqoN+OYQArG2EUWzuODwczdYPhhupBXwR3oX4g41k/BsYfQfZBVzBFEJdWrIDLyAUFWNlfdGIj2BTiAoySfyqmamvmW8bsvc8coiGlZ28UC85/Xqx9wOzjeGoRkCH7PcTMlc9F7SxSthwX/k1VBXmNOHa+HzGOgO/W3k1LDqJbq2wKjZTW3iVEg2VodjxgBLMm0MueSGoI6IuaZSPMyFEM3gGvC2+cDBI2SL/amhiTUa/VDlTVw/IKbSuar9uQ==</D></RSAKeyValue>''';
var xmlDocument = xml.XmlDocument.parse(privateKeyXML);
var modulus = getData(xmlDocument, 'Modulus');
var privateExponent = getData(xmlDocument, 'D');
var p = getData(xmlDocument, 'P');
var q = getData(xmlDocument, 'Q');
var privateKey = RSAPrivateKey(modulus, privateExponent, p, q);

var decryptor = OAEPEncoding(RSAEngine())..init(false, PrivateKeyParameter<RSAPrivateKey>(privateKey)); // Applies SHA-1 by default for both digests, i.e. the OAEP and MGF1 digest, and an empty label
var decrypted = decryptor.process(base64.decode(encryptedBase64)); // encryptedBase64: Base64 encoded ciphertext

print('Decrypted data: ${utf8.decode(decrypted)}');

// key export
print('PKCS#8, PEM: ${CryptoUtils.encodeRSAPrivateKeyToPem(privateKey)}');