【问题标题】:CommonCrypto in iOS producing different results to Rijndael in C#iOS 中的 CommonCrypto 与 C# 中的 Rijndael 产生不同的结果
【发布时间】:2014-03-26 04:42:51
【问题描述】:

是的,所以我有一个 API 正在尝试向其传递密码。我需要将密码作为加密的 AES 256 字符串发送。我已经使用 Rijndael 用 C# 编写了这个过程的工作实现。如下:

AESKey.Text = "WebServices_TestKeyT218adje2s83a"; 
UniqueIV.Text = "T6wfOZgP0Q1uq0gaEHo8ww==";  
pwd1.Text = @"test12";

ASCIIEncoding textConverter = new ASCIIEncoding(); 
// Pad entered password to multiple of 16 
int padLen = 16 - (pwd1.TextLength % 16);

count1.Text = Convert.ToString(padLen);

int totalWidth = pwd1.TextLength + padLen; 
string paddedpwd = pwd1.Text.PadRight(totalWidth, (char)21);

byte[] password = textConverter.GetBytes(pwd1.Text.PadRight(totalWidth, (char)padLen));

// Decode entered IV 
byte[] decodedIV = Convert.FromBase64String(UniqueIV.Text);

string DecodedIVString = System.Text.Encoding.UTF8.GetString(decodedIV);

byte[] aeskey1 = textConverter.GetBytes(AESKey.Text);

string AESKeyBytes = System.Text.Encoding.UTF8.GetString(aeskey1);

//string aeskey = AESKey.Text;

// Create a new Rijndael object 
RijndaelManaged rij = new RijndaelManaged();

// Set a few starting bits... 
rij.IV = decodedIV; 
rij.Key = aeskey1; 
rij.Mode = CipherMode.CBC;

// Generate encryptor... 
ICryptoTransform encryptor = rij.CreateEncryptor();


// Memory to hold encrypted data... 
byte[] encPassword = null;

using (MemoryStream ms = new MemoryStream()) 
{ 
    using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) 
    { 
        cs.Write(password, 0, password.Length); 

        encPassword = new byte[ms.Length]; 
        encPassword = ms.ToArray(); 
    } 
}

// Encode enrypted password 
string encodedPassword = Convert.ToBase64String(encPassword);

EncodedPasswordBox.Text = encodedPassword; 

这有效,并且输出:

V0xd7lMUWfJjlWpXJKzjPw==

被web服务接受没问题。我试图在 Objective-C 中复制这个过程。第一种方法获取密码并进行填充等,然后调用加密方法:

NSString * Password = @"test12";

int padding = 16 - ([Password length] % 16);

int asciiCode = padding;

for(int i=0;i<padding;i++)
{
    Password = [Password stringByAppendingString:[NSString stringWithFormat:@"%c", asciiCode]];
}

NSLog(@"Password after padding: %@", Password);

NSLog(@"Padding: %d", padding);

NSString *base64EncodedString = [[Password dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
NSLog(@"Encoded Padded PWD: %@", base64EncodedString);

NSString * IVString = @"T6wfOZgP0Q1uq0gaEHo8ww==";
NSData * IVData = [self base64DecodeString:IVString];

NSLog(@"IVData: %@", IVData);

NSString * decodedIV = [[NSString alloc] initWithData:IVData encoding:NSASCIIStringEncoding];

NSLog(@"Decoded IV: %@", decodedIV);

NSData * CryptoPass = [self AES256Encryptor1:Password WithKey:@"WebServices_TestKeyT218adje2s83a" iv:decodedIV];

NSString * Pass1 = [self base64EncodeData:CryptoPass];

NSString* newStr = [[NSString alloc] initWithData:CryptoPass encoding:NSASCIIStringEncoding];

NSLog(@"Cryptopass B64: %@", Pass1);

NSLog(@"Cryptopass: %@", CryptoPass);

IV、密钥和密码都与 C# 示例中使用的相同。加密方法如下:

- (NSData *)AES256Encryptor1:(NSString *)dataString WithKey:(NSString *)key iv:(NSString *)iv {

// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

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

NSLog(@"keyPtr: '%s'", keyPtr);

NSData *keyData = [key dataUsingEncoding:NSASCIIStringEncoding];
NSLog(@"keyPtr: '%s'", keyData.bytes);
NSData *dataToEncrypt = [dataString dataUsingEncoding:NSASCIIStringEncoding];
NSData *ivData = [iv dataUsingEncoding:NSASCIIStringEncoding];

NSUInteger dataLength = [dataToEncrypt length];
NSLog(@"Data length: %d", dataLength);

//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;

NSLog(@"Buffer Size: %zu", bufferSize);

void *buffer = malloc(bufferSize);

size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, 0,
                                      keyData.bytes, kCCKeySizeAES256,
                                      ivData.bytes, // initialisation vector
                                      dataToEncrypt.bytes,
                                      dataToEncrypt.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];
} else {
    NSLog(@"Error: %d", cryptStatus);
}

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

但这会返回与 C# 完全不同的东西。我得到以下 base 64 字符串:

faAoHJ/oBGVpi0LHi6fhzrWMT9+z/uqYm1bdHOKrs6o=

现在,显然这完全是错误的长度。我想知道 Common Crypto 是否附加了一个额外的块?它使用的缓冲区大小为 32,但我不知道这是否正确。

如果有人可以就我是否做了一些愚蠢的事情提供一些建议,并可能为我提供一个解决方案或一些关于如何找到真正有用的解决方案的提示!

谢谢!

亚当

编辑:

根据以下 cmets 之一的要求,以下是每个示例的相应输出:

keyData (keyPTR) (Obj-C): WebServices_TestKeyT218adje2s83a

DataToEncrypt (Obj-C):

DataToEncrypt (C#):6164616d31320a0a0a0a0a0a0a0a0a0a

IVData (Obj-C):

IVData (C#): 4fac1f39980fd10d6eab481a107a3cc3

Obj-C 和 C# 预加密中的所有值都相同。在我开始尝试加密任何东西之前,我确保我做对了,所以它应该有所有正确的输入。

编辑 2:

感谢下面的评论者 Zaph 指出我在 Obj-C 中指定了填充,但在 C# 中没有指定,我现在有一个长度正确的字符串。当我现在运行它时,我得到了字符串:

MO8yM8RSN+xEBV6/r6Mx5A==

C#中的字符串为:V0xd7lMUWfJjlWpXJKzjPw==

【问题讨论】:

  • 我没有看到 C# 版本指定了填充,这是默认值吗?它是否使用 PKCS7Padding?
  • 非常好!我已在 Obj-C 中将其更改为 0,现在我有一个正确长度的字符串!它仍然不是正确的字符串,但它现在是一个有效的长度!我会更新问题以反映这一点。
  • 数据长度始终为 16,块大小为 32,所以应该没问题。再次感谢!

标签: c# ios objective-c encryption aes


【解决方案1】:

您尚未显示 keyData、dataToEncrypt、ivData 或要返回的数据的 NSLog 输出。它们与 C# 值相同吗?请将此输出添加到您的问题中。将操作分为三个部分进行调试:准备解密的输入、解密、后处理结果。然后可以将焦点应用到执行不正确的代码上。

我没有看到 C# 版本指定了填充,这是默认设置吗?它是否使用 PKCS7Padding?

如果您不指定填充,则数据必须是块大小的精确倍数。

使用数据转储而不是字符转储检查关键数据。 NSLog(@"keyData: %@", keyData);通常使用 'NSUTF8StringEncoding ` 而不是 NSASCIIStringEncoding。

【讨论】:

    【解决方案2】:

    好的,解决了!

    非常感谢 Zaph,原始问题的评论者为我指出了关于 PKCS7Padding 的正确路径。

    这个问题很简单,尽管我花了一些时间才弄清楚。我传入的是解码后的 IV 字符串,而不是原始 C# 示例中的数据。出错的代码是:

    NSString * decodedIV = [[NSString alloc] initWithData:IVData encoding:NSASCIIStringEncoding];
    

    然后:

    NSData * CryptoPass = [self AES256Encryptor1:Password WithKey:@"key" iv:decodedIV];
    

    因为第一行不应该存在,而我应该直接传递 IVData:

    NSData * CryptoPass = [self AES256Encryptor1:Password WithKey:@"key" iv:IVData];
    

    并修改加密方式如下:

    - (NSData *)AES256Encryptor1:(NSString *)dataString WithKey:(NSString *)key iv:(NSData *)iv {
    
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
    
    // fetch key data
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSASCIIStringEncoding];
    
    NSLog(@"keyPtr: '%s'", keyPtr);
    
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"keyPtr: '%s'", keyData.bytes);
    NSData *dataToEncrypt = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"Data To Encrypt: %@", dataToEncrypt);
    NSData *ivData = iv;
    
    NSUInteger dataLength = [dataToEncrypt length];
    NSLog(@"Data length: %d", dataLength);
    
    //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;
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    
    NSLog(@"Buffer Size: %zu", bufferSize);
    
    void *buffer = malloc(bufferSize);
    
    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, 0,
                                          keyData.bytes, kCCKeySizeAES256,
                                          ivData.bytes, // initialisation vector
                                          dataToEncrypt.bytes,
                                          dataToEncrypt.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];
    } else {
        NSLog(@"Error: %d", cryptStatus);
    }
    
    free(buffer); //free the buffer;
    return nil;
    }
    

    所以,我希望这对其他人有所帮助,因为这让我有点头疼!

    谢谢!

    亚当

    【讨论】:

    • iOS有解密解决方案吗?目前我有 c# 加密字符串,但在 iOS 中解密它时遇到问题。我被困了好几天。
    • 嗨,很遗憾没有。对不起!
    猜你喜欢
    • 2019-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 2020-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多