【问题标题】:AES encryption cross-component Java/Scala and CAES 加密跨组件 Java/Scala 和 C
【发布时间】:2016-08-10 21:01:17
【问题描述】:

我正在尝试在不同系统之间进行跨组件加密。目前,我无法真正匹配输出。 我为 C 使用 openssl/aes/h 库,为 Java/Scala 使用 javax.crypto。 我在更改 C 系统中的代码方面没有太大的灵活性,所以我需要修改 Java/Scala 代码。

#include <string.h> 
#include <openssl/aes.h>

char key[] = "aaaaaaaaaaaaaaaa";

int main(){
 unsigned char text[]="hello";
 unsigned char enc_out[80];
 unsigned char dec_out[80];

 AES_KEY enc_key, dec_key;

 AES_set_encrypt_key(key, 128, &enc_key);
 AES_encrypt(text, enc_out, &enc_key);
 return 0
}

C 代码大致如下所示。使用 AES_set_encrypt_key 和 AES_encrypt 调用,除了在需要时用 0 填充(当密钥不是 128 位时)。对于这个简单的示例,填充应该无关紧要。

在我的 scala 代码中:

  def aes_encrypt(value: String, k: String): Array[Byte] = {
    val cipher = Cipher.getInstance("AES")
    val key = Arrays.copyOf(k.getBytes("UTF-8"), 16)
    val secretKey: SecretKeySpec = new SecretKeySpec(key, 0, key.length, "AES")
    cipher.init(Cipher.ENCRYPT_MODE, secretKey)
    cipher.doFinal(value.getBytes("UTF-8"), 0, value.getBytes("UTF-8").length)
  }

使用密钥 "aaaaaaaaaaaaaaaa" 和消息 "hello" ,在 base64 中

C 代码给出“XaiMOCo7KteRUkejpd8JLA=="

在 Scala/Java 中"7GHRfFgKVdaAiwti36tqDQ=="

我正在尝试匹配输出,但我无法做到。 我为 Scala 代码尝试了不同的算法,但我不确定如何匹配 C 输出。

我试图查看 AES_set_encrypt_key 和 AES_encrypt 调用的文档,但我无法从中挖掘太多。我知道 openssl 库中有更好的用户友好调用,例如 evp_encryption 调用,但我无法摆脱 AES_set_encrypt_key 和 AES_encrypt

【问题讨论】:

  • 您确实应该使用EVP_* 函数,因为它们默认包含填充。而 aes.h 中的函数相当基本,可能根本不应该使用。
  • 你应该使用AES_encrypt和朋友。这是一个纯软件实现,因此您不会享受硬件支持,如 AES-NI。您应该使用EVP_* 函数。请参阅 OpenSSL wiki 上的 EVP Symmetric Encryption and Decryption。事实上,您可能应该使用经过身份验证的加密,因为它提供 机密性和真实性。请参阅 OpenSSL wiki 上的 EVP Authenticated Encryption and Decryption

标签: java c encryption openssl aes


【解决方案1】:

我正在尝试匹配输出,但我无法做到...

对于 OpenSSL,您在 ECB 模式下操作密码,并且您负责填充。我相信 OpenSSL 正在加密由 6 个字符 (hello\0) 和 10 个垃圾字符组成的 16 个字节。

对于 Java,我相信您正在使用带有 AES/ECB/PKCS5Padding 的密码。 value: String 使用 PKCS 填充无条件地填充。填充还意味着 16 字节的明文将扩展为 32 字节的密文,而 OpenSSL 保持在 16 字节。

在这种情况下,最好返回测试向量以查看哪个实现存在分歧。对于 AES,您经常使用 Brian Gladman 的向量。


我试图查看 AES_set_encrypt_key 和 AES_encrypt 调用的文档,但我无法从中挖掘太多。我知道 openssl 库中有更好的用户友好调用,例如 evp_encryption 调用,但我无法摆脱 AES_set_encrypt_key 和 AES_encrypt

OpenSSL 不记录它们,因为它们不希望您使用它们。如果你和AES_encrypt 和朋友在一起,那么你就嫁给了一个纯软件的实现。您有时还会遇到字节序问题,因为 EVP 接口会为您处理这些问题。最后,您将不会享受硬件加速,例如 i686 上的 AES-NI 或 ARMv8 上的 AES。


C 代码给出XaiMOCo7KteRUkejpd8JLA==,在 Scala/Java 中为7GHRfFgKVdaAiwti36tqDQ==

相关,Scala/Java 是正确的如果您的目标是使用 PKCS 填充的 ECB 模式。我可以用Crypto++ 复制Scala/Java 结果(它的StreamTransformationFilter 默认使用PKCS 填充):

int main(int argc, char* argv[])
{
  byte key[] = "aaaaaaaaaaaaaaaa";
  ECB_Mode<AES>::Encryption enc(key, 16);
  string result;

  StringSource ss(string("hello"), true,
                  new StreamTransformationFilter(enc,
                       new Base64Encoder(
                           new StringSink(result))));

  cout << result << endl;

  return 0;
}

来自评论

我会尝试做 AES/ECB/NoPadding 并自己在 Scala 中做填充。

您仍然需要避免 C 程序中的垃圾字符。也许像下面这样的东西会帮助你匹配Java的PKCS #7 padding0x0b应用了11次):

unsigned char text[] = "hello\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";

这是一个用PKCS #7 padding 填充的较大字符串(0x06 应用了 6 次):

unsigned char text[] = "hellohello\x06\x06\x06\x06\x06\x06";

如果字符串长度为16,则应用0x10 16次。


使用 ECB 时,是否需要 IV 向量?

没有。 ECB 模式不需要 IV。它在一个密钥下加密一个 16 字节的块(仅此而已)。

【讨论】:

  • 感谢您的信息。我会尝试做 AES/ECB/NoPadding 并在 Scala 中自己做填充。使用 ECB 时,是否需要 IV 向量?如果是这样,默认的 openssl IV 向量是什么?
  • @Andrew - 确保验证提供者的默认 AES 实现是 AES/ECB/PKCS5Padding。另见Java default Crypto/AES behavior
  • 谢谢jww!我更改为 Cipher.getInstance("AES/ECB/NoPadding") 并用 0 填充消息字节数组,直到它是 16 的倍数。Arrays.copyOf(value.getBytes("UTF-8"), paddedLength) 这给了我相同的输出。如果用 0 填充它和用 0x0b 填充它一样好,我会做更多测试。
猜你喜欢
  • 2020-04-01
  • 2013-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-14
相关资源
最近更新 更多