iOS & .NET 产生不同的 AES256 结果
iOS & .NET Produce Different AES256 Results
我已经在这几天了。我最初(也是最终)的目标是在 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
我这辈子都无法确定造成这种差异的原因。有些事情我已经确认:
iOS和.NET都可以成功解密他们的加密数据。
.NET项目中的代码行:
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
不影响结果。它们可以被评论并且输出是相同的。我认为这意味着它们是默认值。我只是把它们留在里面,以表明我在这个例子中尽可能接近地匹配 iOS 的加密属性。
如果我打印出 iOS NSData 对象 "ivData" 和 "keyData" 中的字节,它会生成与我创建它们相同的字节列表 - 所以我不认为这是初始参数的 C <-> ObjC 桥接问题。
如果我打印出 iOS 变量 "passData" 中的字节,它会打印出与 .NET (88) 相同的单个字节。所以我相当确定他们使用完全相同的数据开始加密。
由于 .NET 代码的简洁性,我 运行 没有明显的实验途径。我唯一的想法是有人可以指出我的 "AES256EncryptData:withKey:iv:" 方法中的问题。该代码已从无处不在的 iOS AES256 代码修改而来,因为我们提供的密钥是字节数组,而不是字符串。我在 ObjC 学习得很好,但对 C 的废话不太满意 - 所以我肯定有可能摸索了所需的修改。
我们将不胜感激所有帮助或建议。
您可能正在处理字符串编码问题。在您的 iOS 代码中,我看到您将字符串作为 UTF-8 传递,这将导致 "X" 的单字节字符串。 .NET 默认使用 UTF-16,这意味着您有一个 "X".
的两字节字符串
您可以使用 How to convert a string to UTF8? 将字符串转换为 .NET 中的 UTF-8 字节数组。您可以尝试在这两种情况下写出纯文本字符串的字节数组,以确定您实际上传递的是相同的字节。
我注意到您使用的是 AES256,但有一个 128 位密钥! 16 字节 x 8 位。你不能指望各种功能来填充相同的键,那是未定义的。
我已经在这几天了。我最初(也是最终)的目标是在 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
我这辈子都无法确定造成这种差异的原因。有些事情我已经确认:
iOS和.NET都可以成功解密他们的加密数据。
.NET项目中的代码行:
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
不影响结果。它们可以被评论并且输出是相同的。我认为这意味着它们是默认值。我只是把它们留在里面,以表明我在这个例子中尽可能接近地匹配 iOS 的加密属性。
如果我打印出 iOS NSData 对象 "ivData" 和 "keyData" 中的字节,它会生成与我创建它们相同的字节列表 - 所以我不认为这是初始参数的 C <-> ObjC 桥接问题。
如果我打印出 iOS 变量 "passData" 中的字节,它会打印出与 .NET (88) 相同的单个字节。所以我相当确定他们使用完全相同的数据开始加密。
由于 .NET 代码的简洁性,我 运行 没有明显的实验途径。我唯一的想法是有人可以指出我的 "AES256EncryptData:withKey:iv:" 方法中的问题。该代码已从无处不在的 iOS AES256 代码修改而来,因为我们提供的密钥是字节数组,而不是字符串。我在 ObjC 学习得很好,但对 C 的废话不太满意 - 所以我肯定有可能摸索了所需的修改。
我们将不胜感激所有帮助或建议。
您可能正在处理字符串编码问题。在您的 iOS 代码中,我看到您将字符串作为 UTF-8 传递,这将导致 "X" 的单字节字符串。 .NET 默认使用 UTF-16,这意味着您有一个 "X".
的两字节字符串您可以使用 How to convert a string to UTF8? 将字符串转换为 .NET 中的 UTF-8 字节数组。您可以尝试在这两种情况下写出纯文本字符串的字节数组,以确定您实际上传递的是相同的字节。
我注意到您使用的是 AES256,但有一个 128 位密钥! 16 字节 x 8 位。你不能指望各种功能来填充相同的键,那是未定义的。