2013-08-16 70 views
0

我试图解密从iOS发送的Android上的备份,并且在方法doFinal中显示异常javax.crypto.BadPaddingException: pad block corrupted来自iOS的Android AES解密和数据:javax.crypto.BadPaddingException:pad block损坏

public String decrypt(byte[] cipherText, SecretKey key, byte [] initialVector) throws Exception { 
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 
    IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector); 
    cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec); 
    cipherText = cipher.doFinal(cipherText); 

    return new String(cipherText, "UTF-8"); 
} 

key和initialVector从iOS以base64字符串发送。相关代码:

public static byte[] decodeWebSafe(String s) throws Base64DecoderException { 
    byte[] bytes = s.getBytes(); 
    return decodeWebSafe(bytes, 0, bytes.length); 
} 

byte[] iv = Base64.decodeWebSafe(enciv); 
byte[] salt = Base64.decodeWebSafe(encsalt); 
byte[] data = Base64.decodeWebSafe(encdata); 
SecretKey key = Security.getExistingKey(password, salt); 
String original = aes.decrypt(data, key, iv); 

约在Security.getExistingKey:

public static SecretKey getExistingKey(String password, byte[] salt) throws Exception{ 
    SecretKey key= null; 
    KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256); 
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 

    byte[] keyBytes=new byte[32]; 
    keyBytes = keyFactory.generateSecret(keySpec).getEncoded(); 
    key= new SecretKeySpec(keyBytes, "AES"); 

    return key; 
} 

THX任何解决方案。

PSThis是我们如何设置在iOS上的加密:

CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, 
kCCOptionPKCS7Padding, 
self.encryptionKey.bytes, kCCKeySizeAES128, 
self.encryptionIV.bytes, [rawData bytes], dataLength, 
/* input */buffer, bufferSize, /* output */&numBytesEncrypted); 

密钥和IV的推导方法:

(NSData *)keyForPassword:(NSString *)password salt:(NSData *)salt { 
NSMutableData * 
derivedKey = [NSMutableData dataWithLength:kCCKeySizeAES128]; 

int result = CCKeyDerivationPBKDF(kCCPBKDF2,   // algorithm 
           password.UTF8String, 
           password.length, 
           salt.bytes,   // salt 
           salt.length,   // saltLen 
           kCCPRFHmacAlgSHA1, // PRF 
           kPBKDFRounds,   // rounds 
           derivedKey.mutableBytes, // derivedKey 
           derivedKey.length); // derivedKeyLen 
} 
+0

我会想象你选择的填充模式不正确,或者派生键的方法在两个平台上不同。你有没有相应的iOS代码可以发布? –

+0

@DuncanJones感谢您的评论,我发布了相关的iOS加密代码。 – lht

+0

请同时显示您在iOS中使用的键和IV派生方法。 –

回答

0

我可以看到在路上几个区别你生成密钥:

  • 在iOS中,生成一个16字节/ 128位的密钥;在Android中它是256位。
  • 在iOS中,密码采用UTF-8编码,而Android则采用每个字符的低8位或全16位(我不知道具体算法的细节)。
  • 在iOS中,您传递的密码长度无效(字符数而不是UTF-8编码的字节数)。

您最好花费一些时间来更好地匹配密钥生成和比较解密前的密钥。

+0

它的工作原理!非常感谢您的建议! – lht

+0

你能否发布最新的代码? –

1

我和我的一个朋友创建了一个可以隐藏消息的iOS和Android应用程序。要使用它,你应该创建的NSData的延伸与下面的代码片段from this website

- (NSData *)AES128EncryptWithKey:(NSString *)key { 

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

    // insert key in char array 
    [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; 

    // the encryption method, use always same attributes in android and iPhone (f.e. PKCS7Padding) 
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, 
              kCCAlgorithmAES128, 
              kCCOptionPKCS7Padding, 
              keyPtr, 
              kCCKeySizeAES128, 
              NULL      /* initialization vector (optional) */, 
              [self bytes], dataLength, /* input */ 
              buffer, bufferSize,  /* output */ 
              &numBytesEncrypted); 
    if (cryptStatus == kCCSuccess) { 

     return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; 
    } 

    free(buffer); 
    return nil; 
} 

- (NSData *)AES128DecryptWithKey:(NSString *)key { 
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise 
    char keyPtr[kCCKeySizeAES128 + [key length]]; // room for terminator (unused) 
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding) 

    // insert key in char array 
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 

    NSUInteger dataLength = [self length]; 
    size_t bufferSize = dataLength + kCCBlockSizeAES128; 
    void *buffer = malloc(bufferSize); 

    size_t numBytesDecrypted = 0; 
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, 
              kCCAlgorithmAES128, 
              kCCOptionPKCS7Padding, 
              keyPtr, 
              kCCKeySizeAES128, 
              NULL      /* initialization vector (optional) */, 
              [self bytes], dataLength, /* input */ 
              buffer, bufferSize,  /* output */ 
              &numBytesDecrypted); 

    if (cryptStatus == kCCSuccess) { 

     return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted]; 
    } 

    free(buffer); 
    return nil; 
} 

+ (NSData *)base64DataFromString: (NSString *)string 
{ 
    unsigned long ixtext, lentext; 
    unsigned char ch, inbuf[4], outbuf[3]; 
    short i, ixinbuf; 
    Boolean flignore, flendtext = false; 
    const unsigned char *tempcstring; 
    NSMutableData *theData; 

    if (string == nil){ 
     return [NSData data]; 
    } 

    ixtext = 0; 
    tempcstring = (const unsigned char *)[string UTF8String]; 
    lentext = [string length]; 
    theData = [NSMutableData dataWithCapacity: lentext]; 
    ixinbuf = 0; 

    while (true){ 
     if (ixtext >= lentext){ 
      break; 
     } 

     ch = tempcstring [ixtext++]; 
     flignore = false; 

     if ((ch >= 'A') && (ch <= 'Z')){ 
      ch = ch - 'A'; 
     } else if ((ch >= 'a') && (ch <= 'z')){ 
      ch = ch - 'a' + 26; 
     } else if ((ch >= '0') && (ch <= '9')){ 
      ch = ch - '0' + 52; 
     } else if (ch == '+'){ 
      ch = 62; 
     } else if (ch == '=') { 
      flendtext = true; 
     } else if (ch == '/') { 
      ch = 63; 
     } else { 
      flignore = true; 
     } 

     if (!flignore){ 
      short ctcharsinbuf = 3; 
      Boolean flbreak = false; 

      if (flendtext){ 
       if (ixinbuf == 0){ 
        break; 
       } 

       if ((ixinbuf == 1) || (ixinbuf == 2)) { 
        ctcharsinbuf = 1; 
       } else { 
        ctcharsinbuf = 2; 
       } 

       ixinbuf = 3; 
       flbreak = true; 
      } 

      inbuf [ixinbuf++] = ch; 

      if (ixinbuf == 4){ 
       ixinbuf = 0; 

       outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4); 
       outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2); 
       outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F); 

       for (i = 0; i < ctcharsinbuf; i++) { 
        [theData appendBytes: &outbuf[i] length: 1]; 
       } 
      } 

      if (flbreak) { 
       break; 
      } 
     } 
    } 

    return theData; 
} 

然后在类中要使用的方法插入顶部的地穴:

#import "NSData+Crypt.h" 

而且比加密你的字符串这样的:

NSData *value = [aString dataUsingEncoding:NSUTF8StringEncoding]; 
NSData *encryptedData = [value AES128EncryptWithKey:myKey]; 
NSString *myString = [encryptedData base64Encoding]; 

和解密数据是这样的:

NSData *myData = [NSData base64DataFromString:_textView.text]; 
NSData *decryptedData = [myData AES128DecryptWithKey:_textField.text]; 
NSString *myString2 = [[NSString alloc] initWithData:decryptedData 
              encoding:NSUTF8StringEncoding]; 

我从website of Matt Gallagher使用的方法base64DataFromString否则,如果您使用

[[NSData alloc] base64EncodedDataWithOptions:NSUTF8StringEncoding]; 

的方法就是可以用> =的iOS 7.0

+0

感谢您的咨询!你是中国人吗?(你的ID @亚历山大)? – lht

+0

嘿,我不是中国人,刚刚在学校学过一些中文。 :D –

+0

每个使用上述代码的人,我用长于16个字符的键来解决问题。更长的键现在与来自@ A.S的android代码兼容。 –

2

这里是Android版本至极正在生成的字符串解密/加密消息,它使用Cipher并生成正确的向量来获得与iOS相同的结果。这在这个线程中对应于@亚历山大的iOS版本。

public class MyCrypter { 

private static String TAG = "MyCrypter"; 

public MyCrypter() { 

} 

/** 
* Encodes a String in AES-128 with a given key 
* 
* @param context 
* @param password 
* @param text 
* @return String Base64 and AES encoded String 
* @throws NoPassGivenException 
* @throws NoTextGivenException 
*/ 
public String encode(Context context, String password, String text) 
     throws NoPassGivenException, NoTextGivenException { 
    if (password.length() == 0 || password == null) { 
     throw new NoPassGivenException("Please give Password"); 
    } 

    if (text.length() == 0 || text == null) { 
     throw new NoTextGivenException("Please give text"); 
    } 

    try { 
     SecretKeySpec skeySpec = getKey(password); 
     byte[] clearText = text.getBytes("UTF8"); 

     //IMPORTANT TO GET SAME RESULTS ON iOS and ANDROID 
     final byte[] iv = new byte[16]; 
     Arrays.fill(iv, (byte) 0x00); 
     IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 

     // Cipher is not thread safe 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec); 

     String encrypedValue = Base64.encodeToString(
       cipher.doFinal(clearText), Base64.DEFAULT); 
     Log.d(TAG, "Encrypted: " + text + " -> " + encrypedValue); 
     return encrypedValue; 

    } catch (InvalidKeyException e) { 
     e.printStackTrace(); 
    } catch (UnsupportedEncodingException e) { 
     e.printStackTrace(); 
    } catch (NoSuchAlgorithmException e) { 
     e.printStackTrace(); 
    } catch (BadPaddingException e) { 
     e.printStackTrace(); 
    } catch (NoSuchPaddingException e) { 
     e.printStackTrace(); 
    } catch (IllegalBlockSizeException e) { 
     e.printStackTrace(); 
    } catch (InvalidAlgorithmParameterException e) { 
     e.printStackTrace(); 
    } 
    return ""; 
} 

/** 
* Decodes a String using AES-128 and Base64 
* 
* @param context 
* @param password 
* @param text 
* @return desoded String 
* @throws NoPassGivenException 
* @throws NoTextGivenException 
*/ 
public String decode(Context context, String password, String text) 
     throws NoPassGivenException, NoTextGivenException { 

    if (password.length() == 0 || password == null) { 
     throw new NoPassGivenException("Please give Password"); 
    } 

    if (text.length() == 0 || text == null) { 
     throw new NoTextGivenException("Please give text"); 
    } 

    try { 
     SecretKey key = getKey(password); 

     //IMPORTANT TO GET SAME RESULTS ON iOS and ANDROID 
     final byte[] iv = new byte[16]; 
     Arrays.fill(iv, (byte) 0x00); 
     IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 

     byte[] encrypedPwdBytes = Base64.decode(text, Base64.DEFAULT); 
     // cipher is not thread safe 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 
     cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec); 
     byte[] decrypedValueBytes = (cipher.doFinal(encrypedPwdBytes)); 

     String decrypedValue = new String(decrypedValueBytes); 
     Log.d(TAG, "Decrypted: " + text + " -> " + decrypedValue); 
     return decrypedValue; 

    } catch (InvalidKeyException e) { 
     e.printStackTrace(); 
    } catch (UnsupportedEncodingException e) { 
     e.printStackTrace(); 
    } catch (NoSuchAlgorithmException e) { 
     e.printStackTrace(); 
    } catch (BadPaddingException e) { 
     e.printStackTrace(); 
    } catch (NoSuchPaddingException e) { 
     e.printStackTrace(); 
    } catch (IllegalBlockSizeException e) { 
     e.printStackTrace(); 
    } catch (InvalidAlgorithmParameterException e) { 
     e.printStackTrace(); 
    } 
    return ""; 
} 

/** 
* Generates a SecretKeySpec for given password 
* @param password 
* @return SecretKeySpec 
* @throws UnsupportedEncodingException 
*/ 
public SecretKeySpec getKey(String password) 
     throws UnsupportedEncodingException { 


    int keyLength = 128; 
    byte[] keyBytes = new byte[keyLength/8]; 
    // explicitly fill with zeros 
    Arrays.fill(keyBytes, (byte) 0x0); 

    // if password is shorter then key length, it will be zero-padded 
    // to key length 
    byte[] passwordBytes = password.getBytes("UTF-8"); 
    int length = passwordBytes.length < keyBytes.length ? passwordBytes.length 
      : keyBytes.length; 
    System.arraycopy(passwordBytes, 0, keyBytes, 0, length); 
    SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); 
    return key; 
} 

public class NoTextGivenException extends Exception { 
    public NoTextGivenException(String message) { 
     super(message); 
    } 

} 

public class NoPassGivenException extends Exception { 
    public NoPassGivenException(String message) { 
     super(message); 
    } 

} 

} 
+0

我已经完成了这个项目,但我会进一步研究加密和解密。非常感谢! – lht