C# 和 OpenSSL : Differing Output for AES Encryption

标签 c# openssl aes

我正在尝试编写一个 C# 方法,该方法将吐出与 openssl 二进制文件相同的加密字符串 (Base64),但我很难让事情匹配起来。

后面还有很多终端输出和 C#...:P

我们将使用非常激动人心的示例,即使用密码“123”加密字符串“a”。

首先 是 openssl,当我提供我的静态盐和密码时(这是命令的理想运行方式,也是我希望我的 C# 输出匹配的方式):

dev@magoo ~# echo -n a | openssl enc -aes-128-cbc -S cc77e2a591358a1c -pass pass:123 -a -p
salt=CC77E2A591358A1C
key=7B2AD689138A44AD32297BBAAA5B0EEE
iv =EC4F0416B2E9A9B2FEEF2E66FF982159
U2FsdGVkX1/Md+KlkTWKHOtnt1ftHSKyWiz6GxkBVck=

Second 是 openssl,当我提供我的静态盐时,以及从该盐派生的 key 和 iv(C+P 从第一个命令的输出中获取)但没有密码(因为甚至文档都说这不是一个好主意):

dev@magoo ~# echo -n a | openssl enc -aes-128-cbc -S cc77e2a591358a1c -K 7b2ad689138a44ad32297bbaaa5b0eee -iv ec4f0416b2e9a9b2feef2e66ff982159 -a -p
salt=E85778B7FFFFFFFF
key=7B2AD689138A44AD32297BBAAA5B0EEE
iv =EC4F0416B2E9A9B2FEEF2E66FF982159
62e3V+0dIrJaLPobGQFVyQ==

这让我觉得很奇怪。将第一个命令中“调试”输出(-p 参数)中的 key 和 iv 值添加到相同的盐中,我不知何故得到了不同的盐! (CC77E2A591358A1C 与 E85778B7FFFFFFFF [这里的 4 个字节的 0xff 似乎很有趣])。

第三个是我的应用程序的输出:

dev@magoo ~# mono aestest.exe "a" "123"
==> INPUT     : a
==> SECRET    : 123
==> SALT      : cc77e2a591358a1c
==> KEY       : 7b2ad689138a44ad32297bbaaa5b0eee
==> IV        : ec4f0416b2e9a9b2feef2e66ff982159
==> ENCRYPTED : 62e3V+0dIrJaLPobGQFVyQ==

因此,当我自己手动指定 key 和 IV(然后以某种方式生成不同的盐)时,C# 与 openssl 命令的输出相匹配,但这似乎是错误的。在我看来,C# 应用程序的输出应该与第一组 OpenSSL 的输出匹配,不是吗?

C#代码:

public static string EncryptString(string plainText, string password)
{
  byte[] salt = Encryption.GetStaticSalt();
  byte[] key, iv;

  Encryption.DeriveKeyAndIV(password, salt, out key, out iv);

  var amAes = new AesManaged();
  amAes.Mode = CipherMode.CBC;
  amAes.KeySize = 128;
  amAes.BlockSize = 128;
  amAes.Key = key;
  amAes.IV = iv;

  var icTransformer = amAes.CreateEncryptor();
  var msTemp = new MemoryStream();

  var csEncrypt = new CryptoStream(msTemp, icTransformer, CryptoStreamMode.Write);
  var sw = new StreamWriter(csEncrypt);
  sw.Write(plainText);
  sw.Close();
  sw.Dispose();

  csEncrypt.Clear();
  csEncrypt.Dispose();

  byte[] bResult = msTemp.ToArray();
  string sResult = Convert.ToBase64String(bResult);

  if (System.Diagnostics.Debugger.IsAttached)
  {
    string debugDetails = "";
    debugDetails += "==> INPUT     : " + plainText + Environment.NewLine;
    debugDetails += "==> SECRET    : " + password + Environment.NewLine;
    debugDetails += "==> SALT      : " + Encryption.ByteArrayToHexString(salt) + Environment.NewLine;
    debugDetails += "==> KEY       : " + Encryption.ByteArrayToHexString(amAes.Key) + " (" + amAes.KeySize.ToString() + ")" + Environment.NewLine;
    debugDetails += "==> IV        : " + Encryption.ByteArrayToHexString(amAes.IV) + Environment.NewLine;
    debugDetails += "==> ENCRYPTED : " + sResult;
    Console.WriteLine(debugDetails);
  }

  return sResult;
}

private static string ByteArrayToHexString(byte[] bytes)
{
  StringBuilder sbHex = new StringBuilder();

  foreach (byte b in bytes)
    sbHex.AppendFormat("{0:x2}", b);

  return sbHex.ToString();
}

public static byte[] GetStaticSalt()
{
  // Just random bytes.
  return new byte[]
  {
    0xcc,
    0x77,
    0xe2,
    0xa5,
    0x91,
    0x35,
    0x8a,
    0x1c
  };
}

// largely hijacked from http://stackoverflow.com/a/8011654/97423
public static void DeriveKeyAndIV(string password, byte[] bSalt, out byte[] bKey, out byte[] bIV)
{
  int keyLen = 16;
  int ivLen = 16;

  byte[] bPassword = Encoding.UTF8.GetBytes(password);

  using (var md5Gen = MD5.Create())
  {
    List<byte> lstHashes = new List<byte>(keyLen + ivLen);
    byte[] currHash = new byte[0];

    int preHashLength = bPassword.Length + bSalt.Length;
    byte[] preHash = new byte[preHashLength];

    Buffer.BlockCopy(bPassword, 0, preHash, 0, bPassword.Length);
    Buffer.BlockCopy(bSalt, 0, preHash, bPassword.Length, bSalt.Length);

    currHash = md5Gen.ComputeHash(preHash);
    lstHashes.AddRange(currHash);

    while (lstHashes.Count < (keyLen + ivLen))
    {
      preHashLength = currHash.Length + password.Length + bSalt.Length;
      preHash = new byte[preHashLength];

      Buffer.BlockCopy(currHash, 0, preHash, 0, currHash.Length);
      Buffer.BlockCopy(bPassword, 0, preHash, currHash.Length, password.Length);
      Buffer.BlockCopy(bSalt, 0, preHash, currHash.Length + password.Length, bSalt.Length);

      currHash = md5Gen.ComputeHash(preHash);
      lstHashes.AddRange(currHash);
    }

    bKey = new byte[keyLen];
    bIV = new byte[ivLen];

    lstHashes.CopyTo(0, bKey, 0, keyLen);
    lstHashes.CopyTo(keyLen, bIV, 0, ivLen);
  }
}

我是不是漏掉了一些非常明显的东西,还是更微妙的东西?我环顾四周,看到了很多关于 C#、OpenSSL 和 AES 的信息,但对这个特定问题一无所知……所以,停一下? ;)

最佳答案

如果您直接指定 key 和 IV,那么盐甚至不会起作用。需要盐来使用 key 派生函数(在 OpenSSL 的情况下为专有 EVP_BytesToKey)将密码短语转换为 key 。因此,您可能会得到无关紧要的盐输出。

现在 1) 中 OpenSSL 的第一个输出包含一个 header (检查 ASCII 值:)、盐和密文,这是十六进制的 base 64 字符串:

53616C7465645F5F CC77E2A591358A1C EB67B757ED1D22B25A2CFA1B190155C9

您的应用程序 3) 和 2) 中的 openssl 命令都输出

EB67B757ED1D22B25A2CFA1B190155C9

所以每一件小事似乎都很好。

关于C# 和 OpenSSL : Differing Output for AES Encryption,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13614982/

相关文章:

c# - 更改列表框中所选项目的背景颜色

mysql - MySQL奇数表显示中的AES加密

c# - 如何将相同的 DataSource 用于多个 DataGridView,并在每个 DataGridView 上应用不同的过滤器?

c# - 我可以将属性与匿名类一起使用吗?

MongoDB 3.0.5 错误 : symbol lookup error: undefined symbol: FIPS_mode_set

c - 与 openssl 库交叉编译时 undefined reference 错误

java - BouncycaSTLe的CMac使用方法

.net - .Net 和 iPhone 之间的 AES 互操作性?

c# - 如何使用 C# 从文件中读取内容并将其用于 ASP.NET 页面上的 HTML 代码?

ios - 在 Windows for iPhone 应用程序上将推送证书转换为 P12 格式