Blowfish 在 Java/Scala 中加密并在 bash 中解密

Blowfish encrypt in Java/Scala and decrypt in bash

我正在尝试构建一个工具来解密在 Scala 应用程序中加密的 bash 中的内容:

但首先,我必须成功地用两种语言编码相同的消息并使它们相等:

给定密码“0123456789abcdef”
(十六进制:“30313233343536373839616263646566”和字节[]:[48、49、50、51、52、53、54、55、56、57、97, 98、99、100、101、102])

scala> import javax.crypto.Cipher
scala> import javax.crypto.spec.SecretKeySpec
scala> val cipher = Cipher.getInstance("Blowfish")
scala> cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("0123456789abcdef".getBytes("utf-8"), "Blowfish"))
scala> javax.xml.bind.DatatypeConverter.printBase64Binary(cipher.doFinal("message".getBytes("utf-8")))
res7: String = K679Jz06jmc=

但我无法在 bash 中使用 openssl 重现相同的内容。

$ echo "message" | openssl enc -a -e -blowfish -nosalt -nopad -k "0123456789abcdef"
LJ3iFJ2/mYk=
$ echo "message" | openssl enc -a -e -blowfish -nosalt -nopad -k "30313233343536373839616263646566"
JkkJgYv3fQg=

有什么提示吗? 谢谢!

标记 OpenSSL 会更准确,它在任何 shell 或根本没有 shell 时都运行相同。

OpenSSL enc 默认情况下进行 基于密码的 加密,密钥(加上 IV)推导类似于(但不完全)PBKDF1。要使用 密钥 而不是密码进行加密,您需要使用带十六进制的大写 -K,并且至少需要指定 -iv 的一部分;另一方面 -nosalt 没用。

此外,echo命令在它回显的数据中添加了一个换行符,'m e s s a g e NL'的加密与[=48=的加密完全不同].某些操作系统和 shells,取决于您使用的 echo 是内置的还是外部的,有办法省略换行符,但它们并不完全相同。 OTOH 所有 POSIX 系统都支持 printf,因此:

$ printf %s message | openssl enc -blowfish -K 30313233343536373839616263646566 -iv 00 -a
K679Jz06jmc=

编辑:Artjom 是正确的; Java/Scala(可能)默认为 ECB,这是一个坏主意;您应该指定 /CBC/CTR 和填充。与 aventurin 不同,我认为您没有理由强迫自己穿上 /NoPadding 的紧身衣; Java /PKCS5Padding 与 OpenSSL 的默认值兼容。对于 CBC 或 CTR,您需要显式设置 IV(将第三个参数设置为 Cipher.init)以获得确定性结果——并且您必须设置 IV, 或检索 默认的随机数,以产生人们通常想要的 decryptable 结果。 OpenSSL enc -blowfish 默认为 CBC,但明确声明 -bf-cbc-bf-ctr 更清楚。或者 -bf-ecb 如果你 真的 想要欧洲央行,如上所述是不推荐的。

首先,如果你想拥有相同的密文,你必须确保 Scala 和 OpenSSL 加密使用具有相同输入参数的相同确定性加密算法。

由于 OpenSSL 使用 CBC 作为默认模式,我们在 Scala 中也这样做。

import javax.crypto.Cipher
import javax.crypto.spec._
import javax.xml.bind.DatatypeConverter

val cipher = Cipher.getInstance("Blowfish/CBC/NoPadding")

val key = new SecretKeySpec(DatatypeConverter.parseHexBinary("0123456789ABCDEF0123456789ABCDEF"), "Blowfish")

val specIv = new IvParameterSpec(DatatypeConverter.parseHexBinary("0000000000000000"))

cipher.init(Cipher.ENCRYPT_MODE, key, specIv)

val enc = cipher.doFinal("messages".getBytes("UTF-8"))

println(DatatypeConverter.printBase64Binary(enc))

请注意,如果没有填充,输入长度必须是 64 位 Blowfish 块长度的倍数。

使用 OpenSSL,您必须明确指定相同的密钥和初始化向量 (IV)

printf %s "messages" | openssl enc -e -blowfish -nopad -K "0123456789ABCDEF0123456789ABCDEF" -iv "0000000000000000" -base64

对于上面的例子,在这两种情况下你都会得到 JSj0k4FwtG8= 作为密文。