C# iOS中的CommonCrypto与C中的Rijndael产生不同的结果#

C# iOS中的CommonCrypto与C中的Rijndael产生不同的结果#,c#,ios,objective-c,encryption,aes,C#,Ios,Objective C,Encryption,Aes,好的,我有一个API,我正试图传递密码给它。我需要将密码作为加密的AES 256字符串发送。我已经使用Rijndael用C#编写了这个过程的工作实现。详情如下: AESKey.Text = "WebServices_TestKeyT218adje2s83a"; UniqueIV.Text = "T6wfOZgP0Q1uq0gaEHo8ww=="; pwd1.Text = @"test12"; ASCIIEncoding textConverter = new ASCIIEncoding(

好的,我有一个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; 
- (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;
}
这是可行的,并输出:

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);
但这会返回与C#完全不同的结果。我得到以下64进制字符串:

faAoHJ/oBGVpi0LHi6fhzrWMT9+z/uqYm1bdHOKrs6o=
很明显,这是完全错误的长度。我想知道公共加密是否附加了一个额外的块?它使用的缓冲区大小为32,但我不知道这是否正确

如果有人能提供一些建议,我是否做了一些愚蠢的事情,并可能为我提供一个解决方案或一些线索,如何达到一个真正有帮助的

谢谢

亚当

编辑:

根据以下评论之一的要求,以下是每个示例的适当输出:

keyData(keyPTR)(Obj-C):WebServices_testkeyt218adge2s83a

数据加密(Obj-C):

数据加密(C#):6164616D31320A0A

IVData(Obj-C):

IVData(C#):4FAC1F3990FD10D6EAB481A107A3CC3

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

编辑2:

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

MO8yM8RSN+xEBV6/r6Mx5A==

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

好的,解决了它

非常感谢原始问题的评论者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];
并将加密方法修改如下:

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; 
- (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;
}
所以,我希望这能帮助其他人,因为这让我有点头疼

谢谢


Adam

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

我看不出C#版本指定了填充,这是默认值吗?它是否使用PKCS7Padding

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


使用数据转储而不是字符转储检查密钥数据。NSLog(@“keyData:%@”,keyData);通常使用“NSUTF8StringEncoding”而不是NSASCIIStringEncoding

我看不出C版本指定了填充,这是默认值吗?它是否使用PKCS7Padding?非常好!在Obj-C中,我已将其更改为0,现在我有一个长度正确的字符串!它仍然不是正确的字符串,但它现在是有效的长度!我将更新问题以反映这一点。数据长度始终为16,块大小为32,这样就可以了。再次感谢!你有iOS中的解密解决方案吗?目前,我有c#加密字符串,但在iOS中解密它时遇到问题。我被困了好几天。嗨,不幸的是没有。很抱歉