I am experiencing an issue when decrypting a string using sunjce:
javax.crypto.Cipher cipher =
javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", new BouncyCastleProvider());
GCMParameterSpec spec = new GCMParameterSpec(Constants.GCM_TAG_BYTES * 8, nonce);
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, dataKey, spec);
cipher.update(ciphertext);
return cipher.doFinal();
if I pass the whole ciphertext to doFinal it works correctly but if I call it correctly it only returns partial string. FOr instance for the input
String jsonExample = "{\"dataType\":\"STRING\",\"strValue\":\"000000\"}";
The decrypted bytes only contain "000000" but if I use
return cipher.doFinal(ciphertext);
and remove the update so it correctly prints the original string. What might be the reason? if I pass an empty byte array to doFinal after the update it also results in the same data loss. I want to know the logic behind it, it passes for small texts but for texts of this size it simply does not work.
this is my input
String jsonExample = "{\"dataType\":\"STRING\",\"strValue\":\"000000\"}";
This is how I am printing the decrypted string
String decryptedString = new String(decrypted, StandardCharsets.UTF_8);
This is how I am passing the input string as bytes to the encrypt function
text = jsonExample.getBytes(StandardCharsets.UTF_8)
this is how I am calling encrypt
GCMParameterSpec spec = new GCMParameterSpec(Constants.GCM_TAG_BYTES * 8, nonce);
try {
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, dek, spec);
byte[] ciphertext = cipher.doFinal(text);
When I use cipher.update(ciphertext)
during decryption followed by cipher.doFinal()
or cipher.doFInal(new byte[0])
it only returns
"000000"
after I use the returned byte[]
to String decryptedString = new String(decrypted, StandardCharsets.UTF_8);
But if I directly call cipher.doFInal(cipherText)
during decryption the result string I get is the original string.
You're not using SunJCE, you're using the BouncyCastle provider. (You are using the Sun/Oracle API -- JCA, Java Crypto Architecture -- if that's what you meant to say, but not the provider SunJCE.)
Most Ciphers in JCA return partial data from each or any update
call (in either encrypt or decrypt direction), thus in general if you use update
you must concatenate that value (or those values if multiple calls) plus any value returned from doFinal
before using the result for anything that needs it to be complete, such as decoding UTF8. (Typically the easiest way is to .write
them all to a ByteArrayOutputStream
, or .put
them all to a ByteBuffer
, but there are other options.) However the SunJCE provider does NOT do this for GCM, in the decryption direction only, apparently because the spec (SP800-38D) calls for the plaintext not to be released if the authentication fails, which can only be determined at .doFinal
time, and then it returns all the plaintext.
The Bouncy provider does 'stream' GCM decryption, arguably in violation of the spec but consistent with usual and traditional JCA behavior, so most of the data is returned from the update
call with only the last few bytes from doFinal
and you need to concatenate these as above -- or, as you've found, just don't use update
at all: if the data all fit in one buffer when encrypting, it should also fit when decrypting.
The Bouncy provider must buffer an amount of data equal to the tag so that it can remove and verify said tag; the "small texts" where your code works -- because decrypt .update
returns nothing -- will be ones up to but not exceeding your tag size, which is apparently 8 bytes.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.