【问题标题】:Java encryption code in c#c#中的Java加密代码
【发布时间】:2012-08-30 05:31:37
【问题描述】:

我在使用 AES 加密算法和 SHA1PRNG 哈希加密和解密字符串的 Android 设备上使用了以下 Java 代码。我希望 Android 设备调用用 C# 编写的 .NET WCF 服务。我一直在到处寻找,试图在 C# 中找到一个可以以与 Java 代码类似的方式加密和解密的等价物,但找不到完全相同的方法。这是两种语言的 Encrypt() 方法:

Java:

public static String encrypt(String seed, String cleartext) throws Exception 
{
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
    sr.setSeed(seed);
    kgen.init(128, sr); // 192 and 256 bits may not be available
    SecretKey skey = kgen.generateKey();

    byte[] rawKey = skey.getEncoded();
    SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(cleartext.getBytes());
    return toHex(encrypted);
}

我在 C# 中创建了类似的东西,它也使用 AES 和 SHA1:

C#:

public static string Encrypt(string seed, string cleartext)
{
  var objAesCrypto = new AesManaged();
  var objHashSha1 = new SHA1Managed();

  var byteHash = objHashSha1.ComputeHash(Encoding.ASCII.GetBytes(seed));
  var truncatedHash = new byte[16];
  Array.Copy(byteHash, truncatedHash, truncatedHash.Length);
  objAesCrypto.Key = truncatedHash;
  objAesCrypto.Mode = CipherMode.ECB;

  var byteBuff = Encoding.ASCII.GetBytes(cleartext);
  return Convert.ToBase64String(objAesCrypto.CreateEncryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length));
}

但是,这有几个问题。如您所见,使用 C# 版本的 SHA1 (SHA1Managed),它返回 20 个字节的散列,而不是 16 个字节。让它传递给 AES 算法的唯一方法是首先将散列截断为 16 个字节。

第二个问题是,尽管两者在各自的环境中都可以正常工作,但当我尝试从 Java 传递加密字符串以及种子时,C# 代码永远无法正确解密它。两种情况下的加密字符串看起来都不一样,甚至长度也不同。 Java 端的典型加密字符串如下所示:F7E8758A2E65518FB49C53BC707288FC(32 个字符长)。而来自 C# 端的具有相同种子的完全相同的加密字符串如下所示:3VysgnYgNi9OJBxL2FP+rQ==(24 个字符长)。

我确信这与我在 C# 中截断哈希的事实有关,但这并不能解释为什么两个加密字符串看起来如此不同。 (我注意到的另一件有趣的事情是,无论我在 C# 端使用什么字符串和种子,它总是 24 个字符长并以两个等号结尾 - 为什么会这样?)

所以,我的问题是,如何让两个环境能够使用相同的种子值解密彼此的加密字符串?我不在乎是否需要在 C# 端使用与 Java 端不同的算法,我只需要 C# 代码能够读取 Java 加密的字符串。

【问题讨论】:

    标签: c# java encryption aes sha1


    【解决方案1】:

    第二个问题是,虽然两者在各自的环境中都可以正常工作,但当我尝试从 Java 传递加密字符串以及种子时,C# 代码永远无法正确解密它。

    您不应该尝试解密哈希。哈希是单向的。

    Java 端的典型加密字符串如下所示:F7E8758A2E65518FB49C53BC707288FC(32 个字符长)。而来自 C# 端的具有完全相同种子的完全相同的加密字符串如下所示:3VysgnYgNi9OJBxL2FP+rQ==(24 个字符长)。

    那是因为您在 Java 中转换为十六进制,但在 C# 中转换为 Base64:

    return toHex(encrypted);
    

    return Convert.ToBase64String(...);
    

    至于种子长度问题 - 同样,您在 Java 和 C# 中做不同的事情。我完全不清楚以这种方式使用 SecureRandom 是否意味着生成与使用来自 SHA1 的直接哈希相同的密钥。

    不过,与其尝试修复这种方法,我建议您重新考虑它 - 它对我来说看起来一点也不安全。你所说的种子不仅仅是种子——它基本上是一个完整的密钥。知道种子的攻击者有效地知道系统的“密码”;您不妨只使用原始字节。

    【讨论】:

    • 虽然 Sun random 在创建后直接播种时确实可以预测“sha1prng”运行,但应用程序不应依赖这一事实。此外,基于 SHA1 的 rng 与 SHA1 不同。
    • 你是对的 - 十六进制字符串与 base 64 字符串的差异对我来说是一种“好啊”的时刻(真的是深夜:)。至于 Java 代码不是很安全,有没有人有更好的例子来说明更安全但同时适用于 Android Java 平台和 C# 平台的东西?
    • @user1635001:嗯,要考虑的主要问题是你想如何保守秘密。你在这里有seed,它实际上是一个密码。当然,你之后会用它做一些事情,但这只是混淆:你将如何保证它的安全?先解决这个问题。
    【解决方案2】:

    Android 似乎使用了固定版本的 SHA1PRNG。 .NET/Java/Android 的 SHA1PRNG 似乎也有很多实现。

    您可能想查看以下链接以了解一些类似问题,以及可能将 Android 中存在的 SHA1PRNG 移植到 C#。 SHA1PRNG in Android - .NET

    【讨论】:

      【解决方案3】:

      据我所知,您的toHex(encrypted);Convert.ToBase64String() 不同。

      【讨论】:

        猜你喜欢
        • 2012-04-08
        • 2020-11-25
        • 2012-04-12
        • 1970-01-01
        • 2011-03-03
        • 1970-01-01
        • 2012-06-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多