react-native-keychain icon indicating copy to clipboard operation
react-native-keychain copied to clipboard

Unknown error: Could not encrypt value for service RN_KEYCHAIN_DEFAULT_ALIAS, message: javax.crypto.IllegalBlockSizeException

Open Diordrzy opened this issue 5 years ago • 19 comments

RNKeychainManager: Unknown error: Could not encrypt value for service RN_KEYCHAIN_DEFAULT_ALIAS, message: javax.crypto.IllegalBlockSizeException

I was getting this when password/value content got bigger, but a content shortening/downsizing made the error "disappear".When the content size is about 9000 bytes, it will not be stored.

react-native-keychain:3.1.3, compileSdkVersion 28 buildToolsVersion '27.0.3'

In debug mode, I found that the error location is the code in the "CipherStorageKeystoreAESCBC.java" file: private byte[] encryptString(Key key, String service, String value) throws CryptoFailedException { try { Cipher cipher = Cipher.getInstance(ENCRYPTION_TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, key); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // write initialization vector to the beginning of the stream byte[] iv = cipher.getIV(); outputStream.write(iv, 0, iv.length); // encrypt the value using a CipherOutputStream CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher); cipherOutputStream.write(value.getBytes("UTF-8")); cipherOutputStream.close(); return outputStream.toByteArray(); } catch (Exception e) { throw new CryptoFailedException("Could not encrypt value for service " + service + ", message: " + e.getMessage(), e); } } Throw this exception when "cipherOutputStream.close()" I need help...

Diordrzy avatar Nov 05 '19 07:11 Diordrzy

I'm getting this issue on the latest release (4.0.5)

NightWarrior avatar Mar 02 '20 08:03 NightWarrior

updates?

joaoedezio avatar Mar 16 '20 12:03 joaoedezio

I'am also getting this error in 5.0.1 version. Here is the logcat:

java.io.IOException: javax.crypto.IllegalBlockSizeException: input must be under 384 bytes
CipherStorageBase:     at javax.crypto.CipherOutputStream.close(CipherOutputStream.java:214)
CipherStorageBase:     at com.oblador.keychain.cipherStorage.CipherStorageBase.encryptString(CipherStorageBase.java:337)

It is happening on real device, Samsung S9+. On Android emulator Pixel 3a it is not happening.

majugurci avatar Apr 05 '20 11:04 majugurci

Also getting this error on Oppo phone, android 9. Mine says the input must be under 384 bytes. Quite limiting...

bauti-defi avatar Apr 16 '20 03:04 bauti-defi

I'm also getting this error when trying to setGenericPassword using RSA as the storage type (AES and FB doesn't throw it). It also happens if I don't set the storage type but set the accessControl to use biometrics (which I assume sets RSA as the storage type by default as well).

I'm using the latest 6.0.0 version.

hugo-advizr avatar Apr 20 '20 10:04 hugo-advizr

Edit: This is fixed for me by upgrading to the latest version (6.0.0)

blankey1337 avatar Jun 02 '20 20:06 blankey1337

I'm running 6.0.0 (attempted to go to 6.1.0, but ran into issue #351). Now, with 6.0.0, I am running into the issue mentioned above (Error: I/O error: javax.crypto.IllegalBlockSizeException: input must be under 384 bytes). I on a Google Pixel 3a XL. My code is as follows:

await Keychain.setGenericPassword( accessToken, refreshToken, { service: 'myService', accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED, securityLevel: Keychain.SECURITY_LEVEL.ANY, accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE, } );

Any suggestions on how to work around this?

rgreen33 avatar Jun 08 '20 13:06 rgreen33

Issue still occurring in 6.1.1.

rgreen33 avatar Jun 09 '20 17:06 rgreen33

Has anyone been able to get past this issue on Android? If so, could you share how you did it?

rgreen33 avatar Jun 10 '20 20:06 rgreen33

I was able to find a workaround using the following settings:

export const saveCookie = (user: string, cookie: Cookie) => {
  return Keychain.setInternetCredentials(
    SERVER_COOKIE,
    user,
    JSON.stringify(cookie),
    {
      rules: Keychain.SECURITY_RULES.NONE,
      storage: Keychain.STORAGE_TYPE.AES,
      accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
    },
  ).catch((error) => console.log(`Error saving cookie to storage: ${error}`));
};

@rgreen33

bauti-defi avatar Jun 11 '20 01:06 bauti-defi

@Bautista-Baiocchi-lora - Thank you for the reply. Our code is very similar. When using AES, I am no longer getting the IllegalBlockSizeException when setting the key (I do still get that error when I attempt to use RSA). However, with AES, I am getting the following error when getting the key (for both, internet and generic): [SyntaxError: JSON Parse error: Unterminated string].

This issue is only occurring on Android. iOS is working fine. Also, shorter strings work fine on Android. So, I think that the issue is related to the size of the string that I am attempting to store (a stringified Azure AD refresh token). I have started another issue (#355), but I am thinking that the issues may be related.

@oblador - Ideas?

rgreen33 avatar Jun 11 '20 11:06 rgreen33

It sounds like your error may not be caused by react-native-keychain @rgreen33 . Can you provide an example stringified and not stringified Azure AD refresh token? Can you also post a code snippet of your storage method?

bauti-defi avatar Jun 12 '20 18:06 bauti-defi

@Bautista-Baiocchi-lora - The length of the stringified token is 944 characters. Below is a code snippet of the code for storage.

  await Keychain.setGenericPassword(
    accessToken,
    refreshToken,
    {
      service: ‘myServiceName,
      accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
      accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE,
      rules: Platform.OS === 'android' ? Keychain.STORAGE_TYPE.NONE : null,
      storage: Platform.OS === 'android' ? Keychain.STORAGE_TYPE.AES : null,
      securityLevel: Platform.OS === 'android' ? Keychain.SECURITY_LEVEL.ANY : null,
    }
  );

Also, immediately following the above code, I have console.log that writes the original string (refreshToken, before being placed in keychain), immediately followed by the the retrieved string (from keychain). See attached screen of these lines from my console. As you can see, the strings match...up to a certain point. Then, the retrieved string gets some odd characters added.

This same code/same token works on iOS. I also store a shorter key (small text key), and it works find on Android. It is just the longer key that seems to have the issue.

By the way, I am also storing an access ID from Azure AD, which has a length of 763 characters. That key is store/retrieved without any issues on iOS and Android. Thus, this is why I am thinking that the length is the issue.

Screen Shot 2020-06-12 at 2 55 03 PM

rgreen33 avatar Jun 12 '20 19:06 rgreen33

@Bautista-Baiocchi-lora To duplicate the exact issue that I am seeing, use the following as your key. Attempt to store/retrieve the string from Android Keystore, using the code that I provided above (or similar). You will see that in the returned value, somewhere in the middle of the string, a bunch of garbage is added. For some reason, long strings are being corrupted. By using the string below, you eliminate the JSON.stringifiy and JSON.parse as being part of the issue.

Ideas on how to get around this? Has anyone else found a workaround for this?

let accessToken = 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-';

rgreen33 avatar Jun 14 '20 13:06 rgreen33

@rgreen33 : "For some reason, long strings are being corrupted." => This reminds me of https://github.com/oblador/react-native-keychain/issues/184. The solution in this issue was to split the token in multiple parts and save these parts separately. I also used the "splitting token" solution and it worked well. I didn't tested every library release to see if this error was fixed. With some later release (can't remember which one) the error still exists and I never removed this splitting solution from my code (actually I'm not using this library anymore, sorry - no offence).

But I would guess that this is another problem?

I have also the same problem/error (Illegal Block Size) on e.g. a Pixel 3 and I'm unable to use RSA anymore. The error sounds like this issue: https://security.stackexchange.com/a/33445, but I'm not sure.

Sorry when everything I mentioned is nonsense. :)

sebk avatar Jun 14 '20 13:06 sebk

@sebk: Thank you for the feedback. I was finally able to get this to work for me...but, not real happy with the solution. Essentially, I split the token into multiple parts (as you suggested). In order to get the biometrics to work (only fire once), I stored the shortest part of the token in RSA, with all other parts stored in AES. Obviously, I had to rebuild the token upon retrieval. It is a bit of a pain and a little clunky, but it does work...just not as seamless on Android, as it is on iOS.

rgreen33 avatar Jun 15 '20 12:06 rgreen33

This was quite a complicated problem to overcome. Splitting my string into multiple chunks and saving, then reassembling after I've retrieved all the chunks was the only way i could solve this issue.

markrickert avatar Dec 09 '20 19:12 markrickert

Thanks @markrickert for your comment, can you share some code so we don't have to reinvent the wheel? or if you just used any useful existing js lib for chunking and unchunking that would be very much appreciated 👌🏼 not to code that 🤮 boilerplate stuff 🤣

Andre-at-LaCapitale avatar Mar 23 '22 21:03 Andre-at-LaCapitale

I believe the general recommendation is to not try and store the actual data but a symmetric key used to encrypt and decrypt the data.

That's how disk encryption such as LUKS works, for example. This way, when you rotate the RSA key (e.g. to change its password) you don't have to reencrypt the whole data.

davux avatar Apr 01 '22 14:04 davux