0

I am trying to create an app in flutter and the sample code which I have is in java.

Here is the sample java code gist https://gist.github.com/kapiljhajhria/72a22ff75e238878f539f7bb21026208

and here is my flutter code gist https://gist.github.com/kapiljhajhria/795d1a7c7cf1c76ca8e327bf8b2f51de

Here is a brief summary of what I am doing

  1. Generate a unique Session Key: AES Random Key 256
  2. Encrypt JSON data using the Session Key from step 1
  3. Generate SHA256 hash of JSON data
  4. Encrypt generated hash from step 3, using session key from step 1.
  5. Encrypt the session key using the public key. Public key provided as certificate.cer file. I copied the String value and added it to the class as a constant in order to make it easier to use. Not sure if this was the best approach.
  6. Created a POST request with 3 Parameters. As shown in the java code. I think I am doing this part correctly.
  7. The response which I will get will be encrypted using the session key from step 1. So i will have to decrypt that response data. Haven't reached this step yet.

I don't have access to the server where this request is being made. Since the post request is being made using web view, I can't figure out a way to get proper error from my request. All I get is web page which says "Invalid Request"

So My first guess is that I am not using public key properly to encrypt my session key. if that part is correct, then I am not encrypting the data properly or my encryption method doesn't match encryption methods used in java code the java code. Maybe the session key which I am generating is not correct.

Any help would be greatly appreciated. Thank you. If you need anything from me then please let me know.

7
  • 3
    Can't you compare the functionality/results of the substeps from Kotlin and Dart code to isolate the problem(s)? At the moment your question doesn't seem focused enough to me. Commented Dec 27, 2021 at 20:27
  • @Topaco I don't have complete java code. Will have to create a java app in order to make such comparison. And If I do that then I won't have to even use the flutter app. Could just use the java to create android app. My last resort will be to try to use method channels in flutter and use this java code as it is. I am hoping that will work, Also I know just basics of java. bare minimum java knowledge. Commented Dec 27, 2021 at 21:18
  • There are some things to raise an eyebrow in the java code. But basically you should get a paper and pencil and compare parameters in both codes and they must match (beware of defaults). The first difference I found is that the java code is using the ECB mode. (with no IV defined) Commented Dec 28, 2021 at 8:11
  • @gusto2 I have tried various options in flutter code without IV. It's on optional parameter, which can be null. But I keep getting error that It can't be null. I don't get it, why does the documentation say that It can be null and why is it optional if we need to specify it every time. This means that I haven't tried an option which doesn't require IV. Maybe this could be it. Or I need to use some other flutter package. Commented Dec 28, 2021 at 12:57
  • It's about the encryption mode AESMode.ecb instead of cbc (this is used in the java code, but that's less secure), in that case IV is not ncessary. There may be more issues, this is just the first I spotted. It would hep you having contact to the people providing the service, so you could be given internal exception (unable to decrypt? wrong parameters? ...) Commented Dec 28, 2021 at 16:20

2 Answers 2

1
+50

I used this document as my reference: https://developers.emsigner.com/signer-gateway/api-reference/signing-documents.html

You need 2 packages: pointycastle and x509, and to import them as follows:

import 'package:pointycastle/export.dart';
import 'package:x509/x509.dart';

Then you need these helper functions:

Uint8List generateSessionKey() {
  final r = Random();
  return Uint8List.fromList(List<int>.generate(32, (_) => r.nextInt(256)));
}

RSAPublicKey parseCert(String pemData) {
  final cert = parsePem(pemData).first as X509Certificate;
  final pub = cert.publicKey as RsaPublicKey;
  return RSAPublicKey(pub.modulus, pub.exponent);
}

Uint8List encryptUsingPublicKey(RSAPublicKey key, Uint8List data) {
  final cipher = PKCS1Encoding(RSAEngine())
    ..init(true, PublicKeyParameter<RSAPublicKey>(key));
  return cipher.process(data);
}

Uint8List encryptUsingSessionKey(Uint8List key, Uint8List data) {
  final cipher = PaddedBlockCipher('AES/ECB/PKCS7')
    ..init(true, PaddedBlockCipherParameters(KeyParameter(key), null));
  return cipher.process(data);
}

Uint8List sha256Digest(Uint8List data) {
  return SHA256Digest().process(data);
}

And you'd build your 3 parameters like this:

  final pem = File('cert2.pem').readAsStringSync();
  final publicKey = parseCert(pem);

  final sessionKey = generateSessionKey();

  final encryptedSessionKey = encryptUsingPublicKey(publicKey, sessionKey);

  final jsonString = json.encode(<String, dynamic>{
    'FileType': 'PDF',
    'SignaturePosition': 'Top-Left',
    'AuthToken': 'some token',
    'File': '',
    'SUrl': 'http://localhost:3000/Success',
    'FUrl': 'http://localhost:3000/Error',
    'CUrl': 'http://localhost:3000/Cancel',
    'ReferenceNumber': 'generate unique reference number',
  });

  final jsonBytes = utf8.encode(jsonString) as Uint8List;

  final encryptedJson = encryptUsingSessionKey(sessionKey, jsonBytes);

  final hash = sha256Digest(jsonBytes);
  final encryptedHash = encryptUsingSessionKey(sessionKey, hash);

  final p1 = base64.encode(encryptedSessionKey);
  final p2 = base64.encode(encryptedJson);
  final p3 = base64.encode(encryptedHash);

BUT, the big problem I see is how you then do the POST, because you want to be in a web page, right? And the normal flutter web view doesn't support an initial post. It does look like there's another package. Just search for flutter webview post.

By the way, if you don't want to use the pointycastle registry, you can rewrite encryptUsingSessionKey without as:

  final cipher = PaddedBlockCipherImpl(
    PKCS7Padding(),
    ECBBlockCipher(AESEngine()),
  )..init(true, PaddedBlockCipherParameters(KeyParameter(key), null));
  return cipher.process(data);

Finally, at least until you get the web view issue understood, you can just use http to do the post. But, let it do the work of encoding the parameters and setting the content type, as follows:

  final response = await http.post(
    Uri.parse('https://somewhere/V3_0/Index'),
    body: <String, String>{
      'Parameter1': p1,
      'Parameter2': p2,
      'Parameter3': p3,
    },
  );
  print(response.statusCode);
  print(response.body);
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks, I will give the above code a try. I have post request sorted out. can't remember the package name right now, but making post request using web view is working right now. Even my usage of public key is correct, I have confirmed it with them by asking them to decode a string which i encrypted using publc key. My guess now is issue with either AES encryption. Current package does have a bug related to AES encryption, i used to workaround from their github issue post but maybe that workaround is not for my case.
It worked. Thanks. I had to modify one part of the code. gist.github.com/kapiljhajhria/c808cbeb2a769df7037229068dafcddf
@kapil I dislike the 'wrapper' packages that try to make use of point castle 'easier' because they obscure and obfuscate so much of the underlying functionality. For example, the package you were using only expects you to use CBC mode - so that's all it allows. What happens if you want to use GCM or, as in this case, ECB? You can't. That's why it's worth the bit of time it takes to learn how to use pointy castle directly.
(Incidentally, pointy castle is of course just a port of bouncy castle - now maintained by the BC folks. Your Kotlin code uses bouncy castle so there are strong similarities between the original Kotlin code and the eventual Dart code.)
@kapil Excellent news! In the end the Dart code looks pretty neat (even in comparison to the original Kotlin), right?
|
0

Have a look at this project, It's an example for the Encryption by Flutter with a good documentation, have 3 types of encryptions:

  1. AES encryptions
  2. Fernet encryptions
  3. Salsa20 encryptions

1 Comment

The linked project does not show how to use AES cipher in ECB mode. (It does CBC and, of course, therefore needs an IV.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.