简体   繁体   English

Cipher.doFinal() 为长字符串返回部分解密字符串

[英]Cipher.doFinal() returning part of decrypted string for a long string

I am experiencing an issue when decrypting a string using sunjce:使用 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.如果我将整个密文传递给 doFinal,它会正常工作,但如果我正确调用它,它只会返回部分字符串。 FOr instance for the input例如对于输入

String jsonExample = "{\"dataType\":\"STRING\",\"strValue\":\"000000\"}";

The decrypted bytes only contain "000000" but if I use解密的字节只包含“000000”,但如果我使用

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.如果我在更新后将一个空字节数组传递给 doFinal,它也会导致相同的数据丢失。 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这就是我将输入字符串作为字节传递给加密 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当我在解密过程中使用cipher.update(ciphertext)后跟cipher.doFinal()cipher.doFInal(new byte[0])它只返回

"000000" after I use the returned byte[] to String decryptedString = new String(decrypted, StandardCharsets.UTF_8); "000000"在我使用返回的byte[]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.但是如果我在解密过程中直接调用cipher.doFInal(cipherText) ,我得到的结果字符串就是原始字符串。

You're not using SunJCE, you're using the BouncyCastle provider.您没有使用 SunJCE,您使用的是 BouncyCastle 提供程序。 (You are using the Sun/Oracle API -- JCA, Java Crypto Architecture -- if that's what you meant to say, but not the provider SunJCE.) (您正在使用 Sun/Oracle API ——JCA,Java 加密体系结构——如果这就是您的意思,而不是提供商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. JCA 中的大多数密码从每个或任何update调用(在加密或解密方向)返回部分数据,因此通常如果您使用update ,您必须连接该值(或那些值,如果多次调用)加上从doFinal返回的任何值,然后再使用任何需要它完成的结果,例如解码 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. (通常最简单的方法是将它们全部ByteArrayOutputStream .write或者.put它们全部写入ByteBuffer ,但还有其他选择。)但是 SunJCE 提供者不会为 GCM 执行此操作,仅在解密方向上,显然是因为spec(SP800-38D)要求认证失败不释放明文,只能在.doFinal时判断,然后返回所有明文。

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. Bouncy 提供程序执行“流式”GCM 解密,可以说这违反了规范,但与通常和传统的 JCA 行为一致,因此大部分数据是从update调用返回的,只有来自doFinal的最后几个字节,您需要连接这些如上所述——或者,正如您所发现的,根本不使用update :如果数据在加密时都适合一个缓冲区,那么在解密时它也应该适合。

The Bouncy provider must buffer an amount of data equal to the tag so that it can remove and verify said tag; Bouncy 提供者必须缓冲与标签相等的数据量,以便它可以删除和验证所述标签; 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.您的代码工作的“小文本”——因为 decrypt .update返回任何内容——将达到但不超过您的标签大小,这显然是 8 个字节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM