为什么使用BouncyCastle解密的文本与输入文本有些不同?

huangapple go评论105阅读模式
英文:

Why with BouncyCastle decrypted text is a bit different from input text?

问题

我在Google上找到了用于在Java中加密/解密字符串的代码:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] input = "test".getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
    0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };

SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");

System.out.println(new String(input));

// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);

byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
System.out.println(new String(cipherText));
System.out.println(ctLength);

// decryption pass
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
ptLength += cipher.doFinal(plainText, ptLength);
System.out.println(new String(plainText));
System.out.println(ptLength);

这是输出的内容(由于无法复制粘贴某些字符,所以是屏幕截图):
输出截图

我的问题是:
为什么第一个输入 "test" 与第二个(解密后的) "test" 不同?
我需要这段代码来加密一个密码并将其保存在TXT文件中,然后从TXT文件中读取此加密密码并解密它。
但如果这两个输出不同,我就无法做到这一点。
第二个问题:
是否可以从加密文本中排除 ";"?
有人能帮助我吗?谢谢!

(请注意,我只提供翻译,不回答问题。)

英文:

I found on Google this code for encrypt/decrypt a string in Java:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] input = "test".getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
System.out.println(new String(input));
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
System.out.println(new String(cipherText));
System.out.println(ctLength);
// decryption pass
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
ptLength += cipher.doFinal(plainText, ptLength);
System.out.println(new String(plainText));
System.out.println(ptLength);

And this is the output (screenshot because I can't copy-paste some characters):
output screenshot

My question is:
Why the first input "test" is different from the second (decrypted) "test"?
I need this code to encrypt a password and save it on a TXT file and then read this encrypted password from the TXT file and decrypt it..
But if these two outputs are different I can't do this.
Second question:
Is it possible to exclude ";" from the encrypted text?
Can someone help me, please? Thanks!

答案1

得分: 4

如果您阅读了getOutputSize()的文档,您会看到它返回要预期的最大明文量。cipher实例无法知道添加了多少填充,因此它会估计得很高。当您使用ECB或CBC模式(或任何其他非流模式)时,您将不得不调整字节数组的大小。

System.out.println(ctLength);

如您所见,ctLength具有正确的大小。使用Arrays.copyOf(plainText, ptLength)来获取正确数量的字节,或者如果您只对字符串感兴趣,可以使用四参数的String构造函数(new String(plainText, 0, ptLength, StandardCharsets.UTF_8))。

密文由随机字符组成。实际上,您在屏幕上看到的内容取决于您的标准字符集。如果您真的需要文本,那么您可以对密文进行base64编码。


ECB模式加密不适用于加密字符串。您应该尝试使用包括设置/存储IV的其他模式。

我建议使用new String(StandardCharsets.UTF_8)String#getBytes(StandardCharsets.UTF_8)来在字符串和字节之间进行转换。如果您不指定字符集,则会使用系统默认字符集,这意味着在所有系统上解密您的密码将无法正常工作。不同的字符集在Linux和Android上默认为UTF-8,而在Windows上的Java SE(仍然是?)默认为Windows-1252(扩展的西欧拉丁)字符集。

绝对不需要使用Bouncy Castle提供程序进行AES加密(兼容的填充字符串为"PKCS5Padding")。


请不要从Google随机获取代码示例。在开始实现加密之前,您需要理解密码学。抓取到安全的代码示例的几率几乎为零,不幸的是。

英文:

If you read the documentation for getOutputSize() then you will see that it returns the maximum amount of plaintext to expect. The cipher instance cannot know how much padding is added, so it guesses high. You will have to resize the byte array when you are using ECB or CBC mode (or any other non-streaming mode).

System.out.println(ctLength);

As you can see, ctLength does have the correct size. Use Arrays.copyOf(plainText, ptLength) to get the right number of bytes, or use the four parameter String constructor (new String(plainText, 0, ptLength, StandardCharsets.UTF_8)) in case you're just interested in the string.

The ciphertext consists of random characters. It actually depends on your standard character set what you see on the screen. If you really need text, then you can base 64 encode the ciphertext.


ECB mode encryption is not suitable to encrypt strings. You should try and use a different mode that includes setting / storing an IV.

I'd use new String(StandardCharsets.UTF_8) and String#getBytes(StandardCharsets.UTF_8) to convert to and from strings. If you don't specify the character set then it uses the system default character set, and that means decrypting your passwords won't work on all systems. The allowed characters also differ with Linux & Android defaulting on UTF-8 while Java SE on Windows (still?) defaults to the Windows-1252 (extended Western-Latin) character set.

There is absolutely no need to use the Bouncy Castle provider for AES encryption (the compatible padding string is "PKCS5Padding").


Please don't grab random code samples from Google. You need to understand cryptography before you start implementing it. The chances that you grab a secure code sample is practically zero unfortunately.

huangapple
  • 本文由 发表于 2020年8月16日 02:22:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/63429465.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定