【问题标题】:AES encryption in iOS and Android, and decryption in C#.NETiOS 和 Android 中的 AES 加密,以及 C#.NET 中的解密
【发布时间】:2012-08-14 13:12:46
【问题描述】:

第一件事。前段时间,我需要在 Android 中使用简单的 AES 加密来加密密码并将其作为参数发送给密码被解密的 .net Web 服务。

以下是我的安卓加密:

    private static String Encrypt(String text, String key)
        throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] keyBytes= new byte[16];
        byte[] b= key.getBytes("UTF-8");
        int len= b.length;
        if (len > keyBytes.length) len = keyBytes.length;
        System.arraycopy(b, 0, keyBytes, 0, len);
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
        cipher.init(Cipher.ENCRYPT_MODE,keySpec,ivSpec);

        byte[] results = cipher.doFinal(text.getBytes("UTF-8"));
        String result = Base64.encodeBytes(results);
        return result;
        }

然后我用 C# 解密它:

        public static string Decrypt(string textToDecrypt, string key)
    {
        System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();

        RijndaelManaged rijndaelCipher = new RijndaelManaged();
        rijndaelCipher.Mode = CipherMode.CBC;
        rijndaelCipher.Padding = PaddingMode.PKCS7;

        rijndaelCipher.KeySize = 0x80;
        rijndaelCipher.BlockSize = 0x80;

        string decodedUrl = HttpUtility.UrlDecode(textToDecrypt);
        byte[] encryptedData = Convert.FromBase64String(decodedUrl);
        byte[] pwdBytes = Encoding.UTF8.GetBytes(key);
        byte[] keyBytes = new byte[0x10];
        int len = pwdBytes.Length;
        if (len > keyBytes.Length)
        {
            len = keyBytes.Length;
        }
        Array.Copy(pwdBytes, keyBytes, len);
        rijndaelCipher.Key = keyBytes;
        rijndaelCipher.IV = keyBytes;
        byte[] plainText = rijndaelCipher.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length);
        return encoding.GetString(plainText);
    }

这就像一个魅力,但是当我尝试在 iOS 中做同样的事情时问题就来了。我是 iphone/ipad 的新开发应用程序,所以我用谷歌搜索了它,几乎所有提供的代码示例如下:

- (NSData *)AESEncryptionWithKey:(NSString *)key {
char keyPtr[kCCKeySizeAES128]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

NSUInteger dataLength = [self length];

size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);

size_t numBytesEncrypted = 0;

CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                      keyPtr, kCCKeySizeAES128,
                                      NULL /* initialization vector (optional) */,
                                      [self bytes], [self length], /* input */
                                      buffer, bufferSize, /* output */
                                      &numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
    //the returned NSData takes ownership of the buffer and will free it on deallocation
    return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}

free(buffer); //free the buffer;
return nil;

}

也许我有点过于乐观了,当时我希望这里能顺利过渡,因为当 Android 向我抛出类似以下内容时:

"EgQVKvCLS4VKLoR0xEGexA=="

然后iOS给我:

"yP42c9gajUra7n0zSEuVJQ=="

希望这只是我忘记了,还是某些设置有误?

[UPDATE] 结果现在显示在 base64 编码之后。

【问题讨论】:

  • android版本是一个URL编码的base64字符串,等于"YHH+gTxyIxvAx1cPFLcP0IEW2HcVHQVi9X11656CFsk="(60 71 fe 81 3c 72 23 1b c0 c7 57 0f 14 b7 0f d0 81 16 d8 77 15 1d 05 62 f5 7d 75 eb 9e 82 16 c9)
  • 糟糕,抱歉我忘了提。我也在对iOS版本的结果进行编码,只是用另一种方法,但编码前的结果不同。
  • 更新问题以显示base64编码后的结果。使用相同的密码和相同的密钥。
  • 你验证过编码前的字节数相等吗?对于 iOS 添加NSLog("%@", self);;

标签: c# android ios aes


【解决方案1】:

首先要注意的是,您在此代码中存在严重的安全问题。您正在获取字符串密码,然后将其放入密钥中。如果该字符串是人类可键入的,那么您的密钥空间就会大大受限(将 AES-128 变成更像 AES-40 或 AES-50,甚至可能更糟)。您需要使用 PBKDF2 对密钥进行加盐和拉伸。请参阅Properly encrypting with AES with CommonCrypto 进行更全面的讨论。

您还存在严重的安全问题,因为您将密钥用作 IV(请参阅下文;这实际上是您出现症状的原因)。这不是选择 IV 并使您的密文可预测的正确方法。使用相同密钥加密的相同明文将给出相同的结果。这类似于根本没有静脉注射。 IV 必须是随机的。有关更多讨论,请参见上面的链接。

现在来看您的实际症状。问题是您在 Java 和 C# 中使用密钥作为 IV,但在 iOS 上使用 0 (NULL) 作为 IV(IV 不是可选的;您只是传递 0)。您需要在所有情况下使用相同的 IV。

【讨论】:

  • 我知道存在一些安全问题,但这只是一个临时解决方案。这只是为了快速解决问题,所以在我们完成最终解决方案之前,我们不会传递可读文本。我现在无法真正测试任何东西,因为这是工作中的一个问题,但是,如果我理解正确,如果我更改以下内容应该可以解决:在 C#-decryption: rijndaelCipher.IV = X;在 Java 加密中: IvParameterSpec ivSpec = new IvParameterSpec(X); ...并将 NULL 参数更改为 X,其中 X ofcause 是相同的值?顺便说一句,非常感谢您的回答...
  • 目前,上述代码中的“X”是除 iOS 之外的任何地方的密钥副本。你可以让它成为 iOS 上的 key,或者让它成为 C# 和 Java 的 0。我不会让它成为一些静态值(除了 0)。
  • 好的,非常感谢您的帮助。我明天试试,把你的答案也标记为“已接受”......
  • 好吧,不得不进行一些调整,但它终于奏效了......非常感谢您的帮助!标记为答案...
  • @RobNapier 我现在有类似的要求。如果我将 IV 设为随机,我将无法在 Android 和 IOS 中为相同的纯文本获得相同的加密文本。对吗?我只需要一种 PHP 解密算法。你能解释一下吗?
猜你喜欢
  • 1970-01-01
  • 2020-12-03
  • 2011-11-22
  • 1970-01-01
  • 2015-09-03
  • 2011-07-14
  • 1970-01-01
  • 2016-06-22
相关资源
最近更新 更多