【问题标题】:Different results when encrypting with OpenSSL使用 OpenSSL 加密时的不同结果
【发布时间】:2017-08-24 07:54:13
【问题描述】:

当我尝试使用 OpenSSL 进行加密时遇到一个奇怪的情况

详情:

  • 我尝试使用“Hello World!”加密文件
  • 密码(字符串):“testtesttesttest”
  • 密码(十六进制):74657374746573747465737474657374

加密文件的终端命令:

  • 纯文本密码

    • openssl aes-128-cbc -e -a -nosalt -iv [some vector] -k [password] -in [input file] -out [output file]

    • openssl aes-128-cbc -e -a -nosalt -iv [some vector] -pass pass:[password] -in [input file] -out [output file]

  • 十六进制格式的密码

    • openssl aes-128-cbc -e -a -nosalt -iv [some vector] -K [HEX password] -in [input file] -out [output file]

因此,命令的唯一区别是一个使用纯文本密码,而另一个使用相同的密码,但采用 HEX 形式。所以在我看来加密的结果应该是一样的。但是 Base64 的输出其实是不一样的。

也许有人知道并可以暗示这里可能是什么情况?

另一个案例是,我现在开发了一个小型应用程序,用于加密黑莓手机上的文件,我希望它与其他加密软件(如 PC 上可用的 OpenSSL)兼容。所以我的应用程序的 Base64 输出与 OpenSSL 使用给定密码生成的输出不同。

示例代码如下所示:

String testKey = "testtesttesttest"; (example)

byte[] aesKeyAsBytes = testKey.getBytes();

AESKey aesKey = new AESKey(aesKeyAsBytes);

AESEncryptorEngine engine = new AESEncryptorEngine(aesKey);

CBCEncryptorEngine cengine=new CBCEncryptorEngine(engine, new InitializationVector(_initVector));

PKCS5FormatterEngine fengine = new PKCS5FormatterEngine(cengine);

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

BlockEncryptor cryptoStream = new BlockEncryptor(fengine, outputStream);

cryptoStream.write(plainText, 0, numOfPlainTextBytes);

cryptoStream.close();

byte[] cipherText = outputStream.toByteArray();

outputStream.close();

return cipherText;

【问题讨论】:

  • 我对 OpenSSL 了解不多,但使用密码可能会应用某种 KDF 并使用结果作为密钥。而十六进制编码的字符串很可能直接用作密钥,因为它正好是 128 位。我想如果你最后传递一个带有一个额外字节的十六进制字符串(例如00),你可能会得到一个错误?
  • @luke-park。如果这是真的,那么这意味着 OpenSSL AES 加密与实现 AES-128-CBC 和其他算法的其他工具不兼容。纯文本密码包含 16 个字符,因此应该是精确的 128 位密钥。
  • 嗯,不是这个意思。这意味着如果您想直接使用该字符串作为键(这是一个坏主意),那么您需要先对其进行十六进制编码。
  • @luke-park。我有点困惑。例如,我加密照片并选择密码“Honeymoon_In_Hawaii”。所以这意味着通过 OpenSSL 加密的文件不能被其他工具解密,反之亦然。我需要记住“Honeymoon_In_Hawaii”的 HEX 形式的情况也看起来不够好。
  • @ilmari-karonen:我已将我的应用程序中的示例代码添加到主题中。但也许这个问题更适合 [security.stackexchange.com] 或其他地方。也许其他人对我们使用的纯文本密码如何成为实际用于加密的密钥有这样的误解。感谢luke-park,现在我明白了我的程序中的实际问题。我需要用 OpenSSL 或其他软件使用的一些 KDF 替换 aesKeyAsBytes = testKey.getBytes() 以使其兼容,反之亦然。

标签: linux encryption openssl cryptography


【解决方案1】:

我使用以下输入作为此场景的测试数据:

Key (Hex): 01010101010101010101010101010101
IV  (Hex): 02020202020202020202020202020202
Plaintext: Hello, World!

在 OpenSSL 中通过以下命令使用上述输入:

openssl aes-128-cbc -K 01010101010101010101010101010101 -iv 02020202020202020202020202020202 -e -a -in in.txt -out out-openssl.txt

给了我一个结果:

wT6aF/PXrGhxEwyX6mNfXA==

使用 C# System.Security.Cryptography 命名空间,它有一个我们可以测试的原始 AES 实现,我使用了以下代码:

using (AesManaged aes = new AesManaged())
{
    aes.KeySize = 128;
    aes.Padding = PaddingMode.PKCS7;

    aes.Key = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
    aes.IV = new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };

    byte[] plaintext = Encoding.UTF8.GetBytes("Hello, World!");
    ICryptoTransform t = aes.CreateEncryptor();
    byte[] ciphertext = t.TransformFinalBlock(plaintext, 0, plaintext.Length);

    Console.WriteLine(Convert.ToBase64String(ciphertext));
}

产生完全相同的base64结果:

wT6aF/PXrGhxEwyX6mNfXA==

这表明,如果您希望完全按照提供的方式使用密钥(例如带有大写 K 的 -K 标志),上述 OpenSSL 命令是正确的。

正如我在 cmets 中提到的,OpenSSL确实在以明文形式提供密码时使用 KDF,详见 this answer

【讨论】:

  • 感谢您的链接。那是有用的阅读。否则,当在 HEX 中使用密钥时,Blackberry SDK 也没有问题。所以我想我只需要接受不可能将纯文本与 OpenSSL 结合使用其他工具,我需要找到另一个软件。 GPG 也超出了范围。嗯……
  • 我认为可能值得了解为什么您不能使用由简单拉丁字符组成的键。使用短语作为实际的加密密钥是一件非常不聪明的事情。
  • 这完全有可能。也许我没有正确使用 Blackberry SDK 加密类。但这我可以研究并改进。但在我看来,如果一个软件实现了 AES-128-CBC,而另一个软件实现了相同的算法,那么它们应该是兼容的,反之亦然。
  • 算法完全兼容。密钥派生只是不同。 -k 参数使输入首先通过 KDF 馈送以产生密钥。 -K 参数直接使用提供的十六进制作为键。实际的 AES 部分完全相同,只是密钥不同。
【解决方案2】:

来自 enc 的手册页,它应该出现在任何 Unixy 系统上(有时在像 1ssl 这样的修改部分下)or current release on the web

-k password

    the password to derive the key from. This is for compatibility with 
    previous versions of OpenSSL. Superseded by the -pass argument.

-kfile filename

    read the password to derive the key from the first line of 
    filename. This is for compatibility with previous versions of 
    OpenSSL. Superseded by the -pass argument.

-K key

    the actual key to use: this must be represented as a string 
    comprised only of hex digits. If only the key is specified, the IV ...

您是否看到“密钥源自的()密码”和“实际密钥”之间有什么区别?

PS:由于-K 不做密钥推导,所以与盐-nosalt -salt -S 相关的参数对其没有影响并被忽略。

【讨论】:

  • 是的。其实我应该调查一下人。我检查了 -help 选项,上面写着:` -k val Passphrase -pass val Passphrase source`
猜你喜欢
  • 2016-01-06
  • 1970-01-01
  • 2018-08-17
  • 1970-01-01
  • 1970-01-01
  • 2018-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多