2012-08-14 119 views
13

首先是第一件事。前段时间,我需要在Android中使用简单的AES加密来加密密码,并将其作为密码被解密的.net Web服务的参数发送。iOS和Android中的AES加密以及C#.NET中的解密。

以下是我的Android加密:

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的漂亮的新开发的应用程序,所以ofcause我GOOGLE了它,几乎提供了所有的代码示例是如下:

- (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==" 

希望它只是我忘了的东西,或者一些设置是错误的?

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

+1

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)'。 – Joe 2012-08-14 13:17:51

+0

Woops,对不起,我忘了提及这一点。我使用另一种方法对iOS版本的结果进行编码,但在编码之前结果不同。 – Morten 2012-08-14 13:21:02

+0

更新了问题以显示base64编码后的结果。使用相同的密码和相同的密钥。 – Morten 2012-08-14 13:45:41

回答

5

首先需要注意的是,您在此代码中存在重大的安全问题。你正在接收一个字符串密码,然后把它放到一个密钥中。如果这个字符串是人为的,那么你已经大幅缩小了你的密钥空间(把AES-128变成更像AES-40或AES-50,甚至更糟)。您需要使用PBKDF2盐和拉伸键。请参阅Properly encrypting with AES with CommonCrypto进行更全面的讨论。

您还有一个重大的安全问题,因为您将您的密钥用作IV(请参阅下面的更多内容;这实际上是导致您的症状的原因)。这不是选择IV并使您的密文可预测的正确方法。用相同的密钥加密的相同明文会得到相同的结果。这类似于根本没有IV。 IV需要是随机的。有关更多讨论,请参阅上述链接。

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

+0

我知道存在一些安全问题,但这只是一个临时解决方案。这只是一个快速解决方案,所以我们没有传递可读的文本,直到我们的最终解决方案完成。我现在无法真正测试任何东西,因为它在工作中是一个问题,但是,如果我理解正确,应该修复如果我更改以下内容: 在C#中--decryption:rijndaelCipher.IV = X; 在Java加密中:IvParameterSpec ivSpec = new IvParameterSpec(X); ...并将NULL参数更改为X,其中X ofcause是相同的值? 顺便说一下,非常感谢您的回答... – Morten 2012-08-14 16:18:34

+0

目前上面的代码中的“X”是除了iOS以外的密钥副本。你可以使它成为iOS上的关键,或者使它成为C#和Java的关键。我不会让它有一些静态值(0除外)。 – 2012-08-14 16:33:48

+0

好的,非常感谢你的帮助。我会明天尝试,并将您的答案标记为“已接受”以及... – Morten 2012-08-14 17:04:36