【问题标题】:iOS & .NET Produce Different AES256 ResultsiOS 和 .NET 产生不同的 AES256 结果
【发布时间】:2015-03-11 23:02:05
【问题描述】:

我已经在这几天了。我最初(也是最终)的目标是在 iOS 上使用 CommonCrypto 使用给定的 IV 和密钥加密密码,然后使用 .NET 成功解密。经过大量的研究和失败后,我将目标缩小到在 iOS 和 .NET 上简单地生成相同的加密字节,然后从那里开始。

我在 .NET(C#,框架 4.5)和 iOS (8.1) 中创建了简单的测试项目。请注意,以下代码并非旨在确保安全,而是在更大的过程中筛选出变量。另外,iOS 是这里的变量。 最终的 .NET 加密代码将由客户端部署,因此由我决定是否将 iOS 加密纳入行列。除非确认这是不可能的,否则 .NET 代码不会更改。

相关的.NET加密代码:

    static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
    {
        byte[] encrypted;
        // Create an Aes object 
        // with the specified key and IV. 
        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Padding = PaddingMode.PKCS7;
            aesAlg.KeySize = 256;
            aesAlg.BlockSize = 128;

            // Create an encryptor to perform the stream transform.
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(Key, IV);

            // Create the streams used for encryption. 
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }
        return encrypted;
    }

相关iOS加密代码:

+(NSData*)AES256EncryptData:(NSData *)data withKey:(NSData*)key iv:(NSData*)ivector
{
Byte keyPtr[kCCKeySizeAES256+1]; // Pointer with room for terminator (unused)
// Pad to the required size
bzero(keyPtr, sizeof(keyPtr));

// fetch key data
[key getBytes:keyPtr length:sizeof(keyPtr)];

// -- IV LOGIC
Byte ivPtr[16];
bzero(ivPtr, sizeof(ivPtr));
[ivector getBytes:ivPtr length:sizeof(ivPtr)];

// Data length
NSUInteger dataLength = data.length;

// See the doc: For block ciphers, the output size will always be less than or equal to the input size plus the size of one block.
// That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);

size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                      keyPtr, kCCKeySizeAES256,
                                      ivPtr,
                                      data.bytes, dataLength,
                                      buffer, bufferSize,
                                      &numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
    return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}

free(buffer);
return nil;
}

.NET中传递pass、key、IV并打印结果的相关代码:

byte[] c_IV = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
byte[] c_Key = { 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
String passPhrase = "X";

// Encrypt
byte[] encrypted = EncryptStringToBytes_Aes(passPhrase, c_Key, c_IV);
// Print result
for (int i = 0; i < encrypted.Count(); i++)
{
    Console.WriteLine("[{0}] {1}", i, encrypted[i]);
}

iOS中传递参数和打印结果的相关代码:

Byte c_iv[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
Byte c_key[16] = { 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
NSString* passPhrase = @"X";
// Convert to data
NSData* ivData = [NSData dataWithBytes:c_iv length:sizeof(c_iv)];
NSData* keyData = [NSData dataWithBytes:c_key length:sizeof(c_key)];
// Convert string to encrypt to data
NSData* passData = [passPhrase dataUsingEncoding:NSUTF8StringEncoding];
NSData* encryptedData = [CryptoHelper AES256EncryptData:passData withKey:keyData iv:ivData];

long size = sizeof(Byte);
for (int i = 0; i < encryptedData.length / size; i++) {
    Byte val;
    NSRange range = NSMakeRange(i * size, size);
    [encryptedData getBytes:&val range:range];
    NSLog(@"[%i] %hhu", i, val);
}

在运行 .NET 代码时,它会在加密后打印出以下字节:

[0] 194
[1] 154
[2] 141
[3] 238
[4] 77
[5] 109
[6] 33
[7] 94
[8] 158
[9] 5
[10] 7
[11] 187
[12] 193
[13] 165
[14] 70
[15] 5

相反,iOS 在加密后会打印以下内容:

[0] 77
[1] 213
[2] 61
[3] 190
[4] 197
[5] 191
[6] 55
[7] 230
[8] 150
[9] 144
[10] 5
[11] 253
[12] 253
[13] 158
[14] 34
[15] 138

我终其一生都无法确定造成这种差异的原因。我已经确认了一些事情:

  1. iOS 和 .NET 都可以成功解密其加密数据。

  2. .NET 项目中的代码行数:

    aesAlg.Padding = PaddingMode.PKCS7;
    aesAlg.KeySize = 256;
    aesAlg.BlockSize = 128;

不影响结果。它们可以被评论并且输出是相同的。我认为这意味着它们是默认值。我只留下它们以表明我在此示例中尽可能匹配 iOS 的加密属性。

  1. 如果我打印出 iOS NSData 对象“ivData”和“keyData”中的字节,它会产生与我创建它们时相同的字节列表 - 所以我认为这不是 C 初始参数的 ObjC 桥接问题。

  2. 如果我打印出 iOS 变量“passData”中的字节,它会打印与 .NET (88) 相同的单个字节。所以我相当肯定他们使用完全相同的数据开始加密。

由于 .NET 代码非常简洁,我已经没有明显的实验途径了。我唯一的想法是有人可能会在我的“AES256EncryptData:withKey:iv:”方法中指出一个问题。该代码已从无处不在的 iOS AES256 代码修改而来,因为我们提供的密钥是字节数组,而不是字符串。我在 ObjC 上学得很好,但对 C 的废话不太满意——所以我肯定有可能搞砸了所需的修改。

我们将不胜感激所有帮助或建议。

【问题讨论】:

  • 只是为了好玩,当您将val 声明为Byte val[1]; 时会发生什么?
  • @Ian 我假设你的意思是它会产生什么样的输出?显然,如果我记录 val[0] 结果是一样的。要打印数组本身,我必须使用“%s”格式化程序。结果是:M,'。 =, æ, ≈, ø, 7, Ê, ñ, ê , ˝, ˝, û, ”, ä。
  • 注意:它们只会为不同的输入产生不同的输出。检查所有输入、模式、密钥长度等。
  • 十六进制记录加密调用之前和之后的所有数据。对于测试使用长度正好是一个块的数据。日志记录将涵盖字符串编码的情况。顺便说一句,十六进制转储规则是因为除其他外,许多编码具有清楚地提供有关数据的信息的模式,例如编码。
  • 我注意到您使用的是 AES256 但有一个 128 位密钥! 16 字节 x 8 位。您不能指望各种功能来填充相同的键,即未定义。以所需的确切长度提供所有输入。

标签: c# ios .net encryption aes


【解决方案1】:

我注意到您使用的是 AES256,但有一个 128 位密钥! 16 字节 x 8 位。您不能指望各种功能来填充相同的键,即未定义。

【讨论】:

  • 你能解释一下吗?我有类似的问题。我正在尝试在 Swift 中使用 CommonCrypto 库,但这并没有生成与在 Objective-C 中生成的相同的输出。此加密输出在 .Net 服务器上进一步解密。
  • 使用 ObjC 和 Swift 代码、示例数据和结果创建一个minimal reproducible example 问题。
【解决方案2】:

您可能正在处理字符串编码问题。在您的 iOS 代码中,我看到您将字符串作为 UTF-8 传递,这将导致“X”的单字节字符串。 .NET 默认使用 UTF-16,这意味着你有一个两字节的字符串“X”。

您可以使用How to convert a string to UTF8? 将您的字符串转换为 .NET 中的 UTF-8 字节数组。您可以尝试在这两种情况下写出纯文本字符串的字节数组,以确定您实际上传递的是相同的字节。

【讨论】:

  • 所以您是说传递“plainText”参数的 .NET StreamWriter 对象假设为 UTF-16 编码,从而产生不同的数据?如果是这种情况,我应该能够简单地使用 iOS 提供的某种形式的 UTF-16 编码——对吗?我已经尝试了提供的所有 NSUTF16* 编码选项,并且使用这些选项中的任何一个对我的 passPhrase 进行编码都会产生与 .NET 不同的结果。 但是,如果字符串没有首先转换为 UTF-8,NSUTF16LittleEndianStringEncoding 会生成与 .NET 相同字节 ([88, 0]) 的数据。这似乎有点相关。
  • 如果您只查看十六进制的前几个字节,则相对容易确定编码是什么。
  • @NextInLine 请注意,NSString 在内部使用 UTF-16 编码。
  • UTF-8 和 ASCII 以及解决方案建议的填充,都产生相同的结果。任何 NSUTF16** 编码都与我在应用填充修复后发布的 .NET 代码的结果匹配。
猜你喜欢
  • 2017-08-10
  • 1970-01-01
  • 2012-10-14
  • 1970-01-01
  • 1970-01-01
  • 2019-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多