react-native-keychain
react-native-keychain copied to clipboard
Unknown error: Could not encrypt value for service RN_KEYCHAIN_DEFAULT_ALIAS, message: javax.crypto.IllegalBlockSizeException
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...
I'm getting this issue on the latest release (4.0.5)
updates?
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.
Also getting this error on Oppo phone, android 9. Mine says the input must be under 384 bytes. Quite limiting...
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.
Edit: This is fixed for me by upgrading to the latest version (6.0.0)
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?
Issue still occurring in 6.1.1.
Has anyone been able to get past this issue on Android? If so, could you share how you did it?
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
@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?
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?
@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.

@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 : "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: 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.
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.
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 🤣
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.