为什么 Java 和 PHP 中的 Blowfish 输出仅相差 2 个字符?

2022-09-04 22:37:11

我有一个PHP和JAVA的河豚加密脚本,反之亦然,直到今天我遇到问题时才工作正常。

在Java和PHP中,相同的内容仅通过2个字符进行不同的加密,这真的很奇怪。

菲律宾比索

wTHzxfxLHdMm/JMFnoh0hciS/JADvFFg

爪哇岛

wTHzxfxLHdMm/JMFnoh0hciS/D8DvFFg
-------------------------^^

如您所见,这两个位置不匹配。不幸的是,该值是一个真正的电子邮件地址,我无法共享它。此外,我无法用我测试过的其他几个值重现问题。我尝试过在Java上更改Base64编码类,但这两者都没有帮助。

PHP的源代码在这里,Java的源代码在这里

我能做些什么来解决这个问题?


答案 1

让我们来看看你的Java代码:

String c = new String(Test.encrypt((new String("thevalue")).getBytes(),
                                   (new String("mykey")).getBytes()));
...
System.out.println("Base64 encoded String:" +
                   new sun.misc.BASE64Encoder().encode(c.getBytes()));

你在这里做的是:

  1. 使用系统的默认编码将纯文本字符串转换为字节
  2. 使用系统的默认编码将密钥转换为字节
  3. 加密字节
  4. 使用系统的默认编码将加密字节转换回字符串
  5. 使用系统的默认编码将加密字符串转换回字节
  6. 使用 Base64 对这些加密字节进行编码。

问题出在步骤 4 中。它假定任意字节数组表示系统默认编码中的字符串,并且将此字符串编码回来会给出相同的字节[]。这对于某些编码(例如系列)有效,但对其他编码无效。在Java中,当某些字节(或字节序列)在给定的编码中不可表示时,它将被其他一些字符替换,稍后用于重新转换的字符将映射到字节63(ASCII)。实际上,文档甚至说:ISO-8859?

当给定字节在默认字符集中无效时,此构造函数的行为未指定。

在你的情况下,根本没有理由这样做 - 只需使用您的方法直接输出的字节将它们转换为Base64。encrypt

byte[] encrypted = Test.encrypt("thevalue".getBytes(),
                                "mykey".getBytes());
System.out.println("Base64 encoded String:"+ new sun.misc.BASE64Encoder().encode(encrypted));

(另请注意,我在这里删除了多余的构造函数调用,尽管这与您的问题无关。new String("...")

要记住的一点是:永远不要将任意字节[](不是来自编码字符串)转换为字符串。加密算法(以及除解密以外的大多数其他加密算法)的输出肯定属于不应转换为字符串的数据类别。

永远不要使用系统的默认编码,如果你想要便携式程序。


答案 2

你的代码对我来说似乎是正确的。

看起来您在其中一个程序的输入中有一个尾随的空格,并且它只是一个。我会告诉你为什么:

这些 4 个字符块中的每一个都表示加密字符串中的 3 个字符。不同的部分(第7个块中的JA和D8)实际上来自一个不同的角色。

wTHz xfxL HdMm /JMF noh0 hciS /JAD vFFg

wTHz xfxL HdMm /JMF noh0 hciS /D8D vFFg

如果我做对了,你的电子邮件地址是19个字符长。其中一个输入字符串中的第 20 个字符是一个空格。


推荐