AES obtiene resultados diferentes en iOS (Obj-C) y Android (Java)

Soy un novato completo para este tipo de cosas de encryption, pero tengo una aplicación Java y un iOS, y quiero que ambos puedan ecrypt un text para un mismo resultado. Yo uso AES. Encontré estos códigos, con una pequeña modificación, por supuesto, pero devuelven un resultado diferente

código iOS:

- (NSData *)AESEncryptionWithKey:(NSString *)key { unsigned char keyPtr[kCCKeySizeAES128] = { 'T', 'h', 'e', 'B', 'e', 's', 't', 'S', 'e', 'c', 'r','e', 't', 'K', 'e', 'y' }; size_t bufferSize = 16; void *buffer = malloc(bufferSize); size_t numBytesEncrypted = 0; const char iv2[16] = { 65, 1, 2, 23, 4, 5, 6, 7, 32, 21, 10, 11, 12, 13, 84, 45 }; CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode | kCCOptionPKCS7Padding,, keyPtr, kCCKeySizeAES128, iv2, @"kayvan", 6, dataInLength, buffer, bufferSize, &numBytesEncrypted); if (cryptStatus == kCCSuccess) { return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; } free(buffer); return nil; } 

y el código Java es:

 public static void main(String[] args) throws Exception { String password = "kayvan"; String key = "TheBestSecretKey"; String newPasswordEnc = AESencrp.newEncrypt(password, key); System.out.println("Encrypted Text : " + newPasswordEnc); } 

y en otra class java ( AESencrp.class ) tengo:

 public static final byte[] IV = { 65, 1, 2, 23, 4, 5, 6, 7, 32, 21, 10, 11, 12, 13, 84, 45 }; public static String newEncrypt(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 = 16; System.arraycopy(b, 0, keyBytes, 0, len); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(IV); System.out.println(ivSpec); cipher.init(Cipher.ENCRYPT_MODE,keySpec,ivSpec); byte[] results = cipher.doFinal(text.getBytes("UTF-8")); String result = DatatypeConverter.printBase64Binary(results); return result; } 

La cadena que quería cifrar es kayvan con la key TheBestSecretKey . y los resultados después de la encoding Base64 son:

para iOS: 9wXUiV+ChoLHmF6KraVtDQ==

para Java: /s5YyKb3tDlUXt7pqA5OFA==

¿Qué debería hacer ahora?

Hice una síntesis con iOS / Android / Node.js AES256 misma encoding de resultados, https://gist.github.com/m1entus/f70d4d1465b90d9ee024

Y aquí está la Versión de Android que está generando la Cadena para descifrar / encriptar Mensajes, usa Cifrado y genera el vector correcto para get el mismo resultado que iOS. Esto corresponde a la versión de iOS de @ 亚历山大 aquí en este hilo.

 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); } } } 

Un amigo mío y yo creamos una aplicación de iOS y Android que puede cifrar posts. Para usarlo, debe crear una extensión de NSData con el siguiente fragment de código de este 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 (fe 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; } 

Luego dentro de la class que desea utilizar los methods de cripta insert en la parte superior:

 #import "NSData+Crypt.h" 

Y que encriptar sus cadenas de esa manera:

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

Y descifre los datos de esta manera:

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

Utilicé el método base64DataFromString desde el website de Matt Gallagher, de lo contrario, si usa

 [[NSData alloc] base64EncodedDataWithOptions:NSUTF8StringEncoding]; 

el método solo está disponible en> = iOS 7.0

Pocas cosas importantes a tener en count al implementar el encryption AES:
1. Nunca use text sin formatting como key de encriptación. Aprieta siempre la key de text sin formatting y luego usa para encriptar.
2. Siempre use Random IV (vector de initialization) para encryption y desencryption. La verdadera aleatorización es importante. En los ejemplos anteriores, no se establece ningún vector de initialization. Esto es un defecto de security.
Hace poco escribí una biblioteca de encryption y desencryption AES de plataforma cruzada para C #, iOS y Android que publiqué en Github. Puedes verlo aquí: https://github.com/Pakhee/Cross-platform-AES-encryption