【问题标题】:Why with BouncyCastle decrypted text is a bit different from input text?为什么使用 BouncyCastle 解密的文本与输入文本有点不同?
【发布时间】:2023-03-05 12:27:01
【问题描述】:

我在 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);

这是输出(截图,因为我不能复制粘贴一些字符): output screenshot

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

【问题讨论】:

    标签: java encryption bouncycastle


    【解决方案1】:

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

    System.out.println(ctLength);
    

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

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


    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 获取随机代码示例。在开始实施之前,您需要了解密码学。不幸的是,您获得安全代码示例的机会几乎为零。

    【讨论】:

    • Arrays.copyOf(...) 的替代方案是合适的 String 构造函数(因为 OP 无论如何都会创建一个字符串)。
    • 在 Linux 上,默认字符集取决于您的语言环境,可以为每个进程或每个用户,尽管现在许多 Linux 最初设置了 utf8 语言环境并且很多人不知道如何改变它们。我敢打赌 Solaris 是一样的,但还没有测试过。对于安卓 IDK。另外,我认为您的“树”(参数)是“四”的拼写错误:-}
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-25
    • 2021-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多