<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
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)}');