Pad Block Corrupted - android

I have to do the following operation:
Encryption VB -> Decryption VB
Encryption Android - Decryption Android
Encryption VB -> Decryption Android
Encryption Android -> decryption VB
So far I succedded do encrypt and decrypt on Android.
When I encrypt in VB and try to decrypt on Android, I get the following exception:
E/Exception: pad block corrupted
I also have to mention that when i encrypt short strings in VB and decrypt them also in VB, everything works well. But when i encrypt a larger array of bytes, the decryption works but the result is not the one expected.
Can somebody give me a hint of how to solve the problem?
Thank you !
Here is my code:
.NET functions
Public Function AES_Encrypt2(ByVal byteArray() As Byte, ByVal key As String, Optional ByVal ShortKey As Boolean = False) As String
Try
Dim FirstKeyBytes() As Byte = Encoding.UTF8.GetBytes(key)
If Not FirstKeyBytes Is Nothing Then
If FirstKeyBytes.Length < 32 Then
Array.Resize(FirstKeyBytes, 32)
End If
End If
Dim KeyBytes() As Byte
If ShortKey Then
KeyBytes = New Byte(15) {}
Array.Copy(FirstKeyBytes, KeyBytes, 16)
Else
KeyBytes = New Byte(31) {}
Array.Copy(FirstKeyBytes, KeyBytes, 32)
End If
Dim InitialVectorBytes() As Byte = New Byte() {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 'Encoding.UTF8.GetBytes("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")
Dim SymmetricKey As New RijndaelManaged()
SymmetricKey.Mode = CipherMode.CBC
SymmetricKey.Padding = PaddingMode.PKCS7
Dim Encryptor As ICryptoTransform = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes)
Dim MemStream As New MemoryStream()
Dim CryptoStream As New CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write)
CryptoStream.Write(byteArray, 0, byteArray.Length)
CryptoStream.FlushFinalBlock()
MemStream.Close()
CryptoStream.Close()
Dim CipherTextBytes As Byte() = MemStream.ToArray()
Dim encryptedString As String = Convert.ToBase64String(CipherTextBytes)
Return encryptedString
Catch ex As Exception
Return String.Empty
End Try
End Function
Public Function AES_Decrypt2(ByVal encryptedString As String, ByVal key As String, Optional ByVal ShortKey As Boolean = False) As String
Try
Dim PlainTextBytes1 As Byte() = Convert.FromBase64String(encryptedString)
Dim FirstKeyBytes() As Byte = Encoding.UTF8.GetBytes(key)
If Not FirstKeyBytes Is Nothing Then
If FirstKeyBytes.Length < 32 Then
Array.Resize(FirstKeyBytes, 32)
End If
End If
Dim KeyBytes() As Byte
If ShortKey Then
KeyBytes = New Byte(15) {}
Array.Copy(FirstKeyBytes, KeyBytes, 16)
Else
KeyBytes = New Byte(31) {}
Array.Copy(FirstKeyBytes, KeyBytes, 32)
End If
Dim SymmetricKey As New RijndaelManaged()
Dim InitialVectorBytes As Byte() = New Byte() {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 'Encoding.UTF8.GetBytes("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")
SymmetricKey.Mode = CipherMode.CBC
SymmetricKey.Padding = PaddingMode.PKCS7
Dim Decryptor As ICryptoTransform = SymmetricKey.CreateDecryptor(KeyBytes, InitialVectorBytes)
Dim MemStream1 As New MemoryStream(PlainTextBytes1)
Dim CryptoStream As New CryptoStream(MemStream1, Decryptor, CryptoStreamMode.Read)
Dim pltxt As Byte() = New Byte(PlainTextBytes1.Length - 1) {}
Dim d As Integer = CryptoStream.Read(pltxt, 0, pltxt.Length)
MemStream1.Close()
CryptoStream.Close()
Dim textConverter As New ASCIIEncoding()
Dim round As String = textConverter.GetString(pltxt, 0, d)
Return round
Catch ex As Exception
Return String.Empty
End Try
End Function
And Android methods:
public static String encrypt(byte[] input, String key) {
try {
byte[] iv = new byte[16];
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
String newKey = "";
if (key.length() >= 32) {
newKey = key.substring(0, 32);
} else {
for (int i = key.length(); i < 32; i++) {
key += "0";
}
newKey = key.substring(0, 32);
}
SecretKeySpec skeySpec = new SecretKeySpec(newKey.getBytes(), "AES");
//skeySpec = new SecretKeySpec(newKey.getBytes(), 0, newKey.length(), "AES");
Cipher fileCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
fileCipher.init(1, skeySpec, paramSpec);
byte[] decrypted = fileCipher.doFinal(input);
byte[] base64enc = Base64.encode(decrypted, 0);
return new String(base64enc);
} catch (Exception e) {
Log.e("Exception", e.getMessage());
}
return null;
}
public static byte[] decrypt(String input, String key) {
try {
byte[] iv = new byte[16];
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
byte[] base64enc = Base64.decode(input.getBytes(), 0);
String newKey = "";
if (key.length() >= 32) {
newKey = key.substring(0, 32);
} else {
for (int i = key.length(); i < 32; i++) {
key += "0";
}
newKey = key.substring(0, 32);;
}
SecretKeySpec skeySpec = new SecretKeySpec(newKey.getBytes(), "AES");
Cipher fileCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
fileCipher.init(2, skeySpec, paramSpec);
int x = base64enc.length;
return fileCipher.doFinal(base64enc);
} catch (Exception e) {
Log.e("Exception: ", e.getMessage());
}
return null;
}

I guess the main issue is that the key generation is different in both pieces of code. Passwords are not keys, you should use either binary, randomly generated keys or a good key derivation mechanism like PBKDF2.
Trying to find a well vetted lib. that does use the same protocol for encryption in .NET and Java (/Android) would also be a good idea.
In general, input to cryptographic algorithms need to be binary. Always compare all inputs of your algorithms using hexadecimal encoding right before executing the algorithm.

Related

Swift AES Encryption throws error while in Android doesn't

I tried to do AES encryption in Swift which I do in Android like this:
public static String Encrypt(String text, String key) {
try {
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"));
return Base64.encodeToString(results, Base64.DEFAULT);
}
catch (Exception ex){return "error"+ex.getMessage();}
}
Below is the equivalent code in Swift 3.2:
func aesEncrypt(key: String, iv: String) throws -> String{
let data = self.data(using: String.Encoding.utf8)
let enc = try AES(key: key.bytes, blockMode: BlockMode.CBC(iv: iv.bytes, padding: Padding.pkcs5).encrypt(data!.bytes)
let encData = NSData(bytes: enc, length: Int(enc.count))
let base64String: String = encData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0));
let result = String(base64String)
return result!}
In Android it doesn't matter for key: I can fill with any string (no length limitations). But when using Swift I have to use a 32 charachter string for key and a 16 charachter string for IV, otherwise it will throw an error.
Here is the Swift usage:
let data = "this is string which I want to be encrypted"
let key = "bbbb98232-a343-4343f-2111"
let iv = "0000000000000000" // lenght = 16 like android code
let encryptedString = data.aesEncrypt(key: key, iv: iv);
Is there maybe some mistake in my Swift code?
You can try the below Swift code for AES encryption. Its String extension.
import Foundation
import CommonCrypto
extension String {
func aesEncrypt(key: String, initializationVector: String, options: Int = kCCOptionPKCS7Padding) -> String? {
if let keyData = key.data(using: String.Encoding.utf8),
let data = self.data(using: String.Encoding.utf8),
let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(options)
var numBytesEncrypted: size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
initializationVector,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters)
return base64cryptString
} else {
return nil
}
}
return nil
}
}

iOS Triple DES encryption generating a different String to Android

I utilised TripleDES encryption in my Android app and it returns the expected string as required. However, when implementing the same feature in iOS using CommonCrypto, the returned string is different than what was expected.
public String encrypt(String message, String secretKey) throws Exception {
MessageDigest md = MessageDigest.getInstance("md5");
byte[] digestOfPassword = md.digest(secretKey.getBytes("utf-8"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
KeySpec keySpec = new DESedeKeySpec(keyBytes);
SecretKey key = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainTextBytes = message.getBytes("utf-8");
byte[] buf = cipher.doFinal(plainTextBytes);
byte[] base64Bytes = Base64.encodeBase64(buf);
String base64EncryptedString = new String(base64Bytes);
return base64EncryptedString;
}
iOS Code
+ (NSData*)transformData:(NSData*)inputData operation:(CCOperation)operation withPassword:(NSString*)password
{
NSData* key = [self keyFromPassword:password];
//NSData* iv = [self ivFromPassword:password];
NSMutableData* outputData = [NSMutableData dataWithLength:(inputData.length + kCCBlockSize3DES)];
size_t outLength;
CCCryptorStatus result = CCCrypt(operation, kCCAlgorithm3DES, kCCAlgorithmDES, key.bytes, key.length, nil, inputData.bytes, inputData.length, outputData.mutableBytes, outputData.length, &outLength);
if (result != kCCSuccess)
return nil;
[outputData setLength:outLength];
return outputData;
}
+ (NSData*)keyFromPassword:(NSString*)password
{
NSString* key = [password copy];
int length = kCCKeySize3DES;
while (key.length < length)
key = [key stringByAppendingString:password];
if (key.length > length)
key = [key substringToIndex:length];
return [key dataUsingEncoding:NSUTF8StringEncoding];
}
+ (NSData*)ivFromPassword:(NSString*)password
{
NSString* key = [password copy];
int length = 8;
while (key.length < length)
key = [key stringByAppendingString:password];
if (key.length > length)
key = [key substringToIndex:length];
return [key dataUsingEncoding:NSUTF8StringEncoding];
}
It may be helpful to note that the key being used is a 20 byte key. I am limited to the 20-byte key. The android app automatically takes care of this limitation with Array.copyOf function, however, despite zero padding and repeating the first characters of the key as the last eight characters, I still cannot replicate the output in iOS.
Any help will be appreciated.

Matching iOS & Android AES - bad padding

I have a strange issue trying to match decryption between an existing iOS app, a .net server, and and Android app that I'm working on. I've checked that my program outputs byte for byte the same encryption as the iOS and decrypts it's own packets perfectly. It appears that the server is able to decrypt the packets sent from the Android app, but when I try to decode the packets from the server I'm getting a BadPaddingException on the Android, whereas the iOS version decodes properly. I've also checked that the Key and IV are byte identical.
edit: I've added the server side code (part of a UDP socket listener) from my client, at first glance it looks like padding has not been defined properly, but my research says the the default is PKCS7, so I'm still confused as to what's causing the problem.
I've tested message lengths (coming from the server) before and after decryption and I see 2 different messages. one is a null keep alive message of 16 bytes before decryption, 0 bytes after decryption. The second message is 128 bytes before decryption and 112 bytes after decryption, in iOS. Both fail to decrypt in Android.
iOS:
+ (NSData*)decryptData:(NSData*)data key:(NSData*)key iv:(NSData*)iv;
{
int FBENCRYPT_BLOCK_SIZE = kCCBlockSizeAES128;
int FBENCRYPT_KEY_SIZE = kCCKeySizeAES256;
// setup key
unsigned char cKey[FBENCRYPT_KEY_SIZE];
bzero(cKey, sizeof(cKey));
[key getBytes:cKey length:FBENCRYPT_KEY_SIZE];
// setup iv
char cIv[FBENCRYPT_BLOCK_SIZE];
bzero(cIv, FBENCRYPT_BLOCK_SIZE);
if (iv) {
[iv getBytes:cIv length:FBENCRYPT_BLOCK_SIZE];
}
NSData* Result = nil;
// setup output buffer
size_t bufferSize = [data length] + FBENCRYPT_BLOCK_SIZE;
void *buffer = malloc(bufferSize);
// do decrypt
size_t decryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
cKey,
kCCKeySizeAES256,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&decryptedSize);
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
} else {
free(buffer);
NSLog(#"[ERROR] failed to decrypt| CCCryptoStatus: %d", cryptStatus);
}
return result;
}
Android:
byte[] decryptData(byte[] data, byte[] key, byte[] iv)
{
static int FBENCRYPT_BLOCK_SIZE = 16; //(kCCBlockSizeAES128)
static int FBENCRYPT_KEY_SIZE = 32; //(kCCKeySizeAES256)
// setup key
byte[] cKey = new byte[FBENCRYPT_KEY_SIZE];
Arrays.fill(cKey, (byte) 0x00);
int num = FBENCRYPT_KEY_SIZE;
if (key.length<num)
num = key.length;
System.arraycopy(key, 0, cKey, 0, num);
// setup iv
byte[] cIv = new byte[FBENCRYPT_BLOCK_SIZE];
Arrays.fill(cIv, (byte) 0x00);
if (iv!=null) {
System.arraycopy(iv, 0, cIv, 0, iv.length);
}
Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
SecretKeySpec skeySpec = new SecretKeySpec(cKey, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(cIv);
aesCipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
byte[] byteCipherText = aesCipher.doFinal(data);
return byteCipherText;
}
Server C#:
public enum AESBitCounts
{
AES64Bit = 8,
AES128Bit = 16,
AES256Bit = 32
}
public static byte[] Encrypt(byte[] RawPayload, byte[] key, AESBitCounts AESBitCount)
{
Symmetric sym = new Symmetric(Symmetric.Provider.Rijndael, false);
//sym.mcrypto.Padding = System.Security.Cryptography.PaddingMode.None;
sym.IntializationVector = new Data(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
Data deckey = new Data();
deckey.MinBytes = (Int32)AESBitCount;
deckey.MaxBytes = (Int32)AESBitCount;
deckey.Bytes = key;
Byte fred = deckey.Bytes[0];
Data encrypted = sym.Encrypt(new Data(RawPayload), deckey);
return encrypted.Bytes;
}
It turns out that there is one crucial differance between CCCrypt and Cipher, CCCrypt will return whatever data it is able to decrypt, while Cipher, once it get's a padding error, will not return any data. It seems that my client's server side script was mangling the padding block so that any message shorter than 16 bytes was being decrypted on iOS as null, and in the case of the main message was simply dropping the padding block. I was able to duplicate the result of the iOS code by chopping the last 16 bytes before decoding, returning null if no bytes are left, and switching from using Cipher.doFinal to Cipher.update, which doesn't expect a final padding block to be present.
Edit: I actually didn't need to eliminate the last 16 bytes. Just switching from doFinal to update does the trick.

Equivalent of spongycastle encryption for ios

This has stumped me - the following code uses SpongyCastle's encryption/decryption for Android - I am trying to achieve cross-platform encryption/decryption for iOS.
The following code (from Android) works a treat, AES 128bit CBC with PKCS7Padding, using a supplied salt and password, which the salt is stored on the mysql database, the password is by the end-user, the following code is adapted from this answer by kelhoer.
The reason I used AES128bit is that AES256 is not available in iOS 4+, it was introduced in iOS5+, and having to dip the toe into using openssl to generate a derived key and initialization vector (iv), it was dicey as learnt that Apple rejects apps that are statically linked with openssl library.
Since the platform is based on iOS 4.2+, having resorted to bundling and statically linking the openssl library seems rather, over-kill and would preferably use the CommonCryptor library.
Here's the Android version with Spongycastle code in place:
private static void encrypt(InputStream fin,
OutputStream fout,
String password,
byte[] bSalt) {
try {
PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(
new SHA256Digest()
);
char[] passwordChars = password.toCharArray();
final byte[] pkcs12PasswordBytes =
PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
pGen.init(pkcs12PasswordBytes, bSalt, ITERATIONS);
CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
ParametersWithIV aesCBCParams =
(ParametersWithIV) pGen.generateDerivedParameters(128, 128);
aesCBC.init(true, aesCBCParams);
PaddedBufferedBlockCipher aesCipher =
new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());
aesCipher.init(true, aesCBCParams);
byte[] buf = new byte[BUF_SIZE];
// Read in the decrypted bytes and write the cleartext to out
int numRead = 0;
while ((numRead = fin.read(buf)) >= 0) {
if (numRead == 1024) {
byte[] plainTemp = new byte[
aesCipher.getUpdateOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
final byte[] plain = new byte[offset];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
fout.write(plain, 0, plain.length);
} else {
byte[] plainTemp = new byte[aesCipher.getOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
int last = aesCipher.doFinal(plainTemp, offset);
final byte[] plain = new byte[offset + last];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
fout.write(plain, 0, plain.length);
}
}
fout.close();
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void decrypt(InputStream fin,
OutputStream fout,
String password,
byte[] bSalt) {
try {
PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(
new SHA256Digest()
);
char[] passwordChars = password.toCharArray();
final byte[] pkcs12PasswordBytes =
PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
pGen.init(pkcs12PasswordBytes, bSalt, ITERATIONS);
CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
ParametersWithIV aesCBCParams =
(ParametersWithIV) pGen.generateDerivedParameters(128, 128);
aesCBC.init(false, aesCBCParams);
PaddedBufferedBlockCipher aesCipher =
new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());
aesCipher.init(false, aesCBCParams);
byte[] buf = new byte[BUF_SIZE];
// Read in the decrypted bytes and write the cleartext to out
int numRead = 0;
while ((numRead = fin.read(buf)) >= 0) {
if (numRead == 1024) {
byte[] plainTemp = new byte[
aesCipher.getUpdateOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
// int last = aesCipher.doFinal(plainTemp, offset);
final byte[] plain = new byte[offset];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
fout.write(plain, 0, plain.length);
} else {
byte[] plainTemp = new byte[
aesCipher.getOutputSize(numRead)];
int offset =
aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
int last = aesCipher.doFinal(plainTemp, offset);
final byte[] plain = new byte[offset + last];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
fout.write(plain, 0, plain.length);
}
}
fout.close();
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
}
However under iOS 4.2 (Working with XCode) I cannot figure out how to do the equivalent,
This is what I have tried under Objective C, with the goal of decrypting data from the Android side, stored in mysql database, to test this out:
+(NSData*) decrypt:(NSData*)cipherData
userPassword:(NSString*)argPassword
genSalt:(NSData*)argPtrSalt{
size_t szPlainBufLen = cipherData.length + (kCCBlockSizeAES128);
uint8_t *ptrPlainBuf = malloc(szPlainBufLen);
//
const unsigned char *ptrPasswd =
(const unsigned char*)[argPassword
cStringUsingEncoding:NSASCIIStringEncoding];
int ptrPasswdLen = strlen(ptrPasswd);
//
NSString *ptrSaltStr = [[NSString alloc]
initWithData:argPtrSalt
encoding:NSASCIIStringEncoding];
const unsigned char *ptrSalt =
(const unsigned char *)[ptrSaltStr UTF8String];
NSString *ptrCipherStr =
[[NSString alloc]initWithData:cipherData
encoding:NSASCIIStringEncoding];
unsigned char *ptrCipher = (unsigned char *)[ptrCipherStr UTF8String];
unsigned char key[kCCKeySizeAES128];
unsigned char iv[kCCKeySizeAES128];
//
//int EVP_BytesToKey(const EVP_CIPHER *type,const EVP_MD *md,
//const unsigned char *salt, const unsigned char *data,
//int datal, int count, unsigned char *key,unsigned char *iv);
int i = EVP_BytesToKey(EVP_aes_128_cbc(),
EVP_sha256(),
ptrSalt,
ptrPasswd,
ptrPasswdLen,
PBKDF2_ITERATIONS,
key,
iv);
NSAssert(i == kCCKeySizeAES128,
#"Unable to generate key for AES");
//
size_t cipherLen = [cipherData length];
size_t outlength = 0;
//
CCCryptorStatus resultCCStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key,
kCCBlockSizeAES128,
iv,
ptrCipher,
cipherLen,
ptrPlainBuf,
szPlainBufLen,
&outlength);
NSAssert(resultCCStatus == kCCSuccess,
#"Unable to perform PBE AES128bit decryption: %d", errno);
NSData *ns_dta_PlainData = nil;
if (resultCCStatus == kCCSuccess){
ns_dta_PlainData =
[NSData dataWithBytesNoCopy:ptrPlainBuf length:outlength];
}else{
return nil;
}
return ns_dta_PlainData;
}
Have supplied the data and user's password and get a return code from CCCrypt as -4304 which indicates not successful and bad decoding.
I have thought that perhaps the encoding scheme would be throwing off the CommonCryptor's decryption routing hence the long-winded way of converting to NSASCIIStringEncoding.
The Salt is stored along with the cipher data, and is 32bytes in length.
What am I missing in this regard, bearing in mind, am weak on cryptography.
I have taken the liberty in coding a direct port of the PKCS12Parameters generator as used on the Android side, the gist for this header is above.
The implementation is direct copy also, as found here, the password, is converted to PKCS12 equivalent - unicode, big-endian, with two extra zeros padded on to the end.
The Generator generates the derived key and iv via performing the number of iterations, in this case, 1000, as is on the Android side, using the SHA256 Digest, the final generated key and iv is then used as the parameters to CCCryptorCreate.
Using the following code sample is not working either, it ends with -4304 upon a call to CCCryptorFinal
The code excerpt is as shown:
#define ITERATIONS 1000
PKCS12ParametersGenerator *pGen = [[PKCS12ParametersGenerator alloc]
init:argPassword
saltedHash:argPtrSalt
iterCount:ITERATIONS
keySize:128
initVectSize:128];
//
[pGen generateDerivedParameters];
//
CCCryptorRef decryptor = NULL;
// Create and Initialize the crypto reference.
CCCryptorStatus ccStatus = CCCryptorCreate(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
pGen.derivedKey.bytes,
kCCKeySizeAES128,
pGen.derivedIV.bytes,
&decryptor
);
NSAssert(ccStatus == kCCSuccess,
#"Unable to initialise decryptor!");
//
size_t szPlainBufLen = cipherData.length + (kCCBlockSizeAES128);
// Calculate byte block alignment for all calls through to and including final.
size_t szPtrPlainBufSize = CCCryptorGetOutputLength(decryptor, szPlainBufLen, true);
uint8_t *ptrPlainBuf = calloc(szPtrPlainBufSize, sizeof(uint8_t));
//
// Set up initial size.
size_t remainingBytes = szPtrPlainBufSize;
uint8_t *ptr = ptrPlainBuf;
size_t movedBytes = 0;
size_t totalBytesWritten = 0;
// Actually perform the encryption or decryption.
ccStatus = CCCryptorUpdate(decryptor,
(const void *) cipherData.bytes,
szPtrPlainBufSize,
ptr,
remainingBytes,
&movedBytes
);
NSAssert(ccStatus == kCCSuccess,
#"Unable to update decryptor! Error: %d", ccStatus);
ptr += movedBytes;
remainingBytes -= movedBytes;
totalBytesWritten += movedBytes;
//
// Finalize everything to the output buffer.
CCCryptorStatus resultCCStatus = CCCryptorFinal(decryptor,
ptr,
remainingBytes,
&movedBytes
);
totalBytesWritten += movedBytes;
if(decryptor) {
(void) CCCryptorRelease(decryptor);
decryptor = NULL;
}
NSAssert(resultCCStatus == kCCSuccess,
#"Unable to perform PBE AES128bit decryption: %d", resultCCStatus);
The funny thing, the decryption works, the final call to CCCryptorFinal returns 0 if I substitute the kCCOptionPKCS7Padding for 0x0000 at the start of CCCryptorCreate, i.e no padding. Alas, the data is not what I expected, still totally scrambled regardless when that "does not work".
It is failing somewhere, so if anyone has any better ideas on how to achieve the equivalent, I would be delighted to hear other opinions.
Either its change the mechanism on the Android side to make it "cross-platform" compatible with iPhone or seek an alternative cryptographic solution to be compatible on both ends at the expense of weaker cryptography on both sides of platforms used for making data interchange portable.
The input data as supplied:
Base64 encoded cipher, with the salt and the cipher separated by ':' tnNhKyJ2vvrUzAmtQV5q9uEwzzAH63sTKtLf4pOQylw=:qTBluA+aNeFnEUfkUFUEVgNYrdz7enn5W1n4Q9uBKYmFfJeSCcbsfziErsa4EU9Cz/pO0KE4WE1QdqRcvSXthQ==
The password supplied is f00b4r
The original string is The quick brown fox jumped over the lazy dog and ran away
Right, I have had to scrap the encryption algorithm on the Android side, which posed a challenge, to find one that is cross-platform compatible.
I have read up a lot about Rob Napier's RNCryptor, and after googling for an Android equivalent, in which I found JNCryptor , I took the plunge and employed RNCryptor on the iOS side.
Forked the JNCryptor code on github to add an enhancement in being able to specify custom settings, and to use SpongyCastle, for older versions of Android. From there on, both platforms were able to encrypt/decrypt interchangeably.
The reason I enhanced JNCryptor, was the iteration count for the PKDBF2 function was quite too high - 10,000 and was default value (since the code will be running on older handsets - it seized up - great if you have dual/quad core!), and needed to override the iteration count to be more "bearable" - 1,000. Using the custom setting was possible with RNCryptor.
Thanks to both Rob Napier and Duncan Jones for their work!

AES Encryption in iOS and Java

I am a newbie to this encryption. I am creating an application for both android and iOS in which i have to encrypt(using AES Encryprtion) a file at server side and decrypt at client side in both iOS and Android App. I got code in internet to perform AES encryption and decryption for both Android and iOS, they are working fine. Server side they are using java. But the problem is Java Encrypted File cant be decrypted by iOS program, even I got the same filesize, But the file is not in correct format. I posted the code below...
Java Encryption and Decryption:
public static byte[] encrypt(byte[] data, byte[] key, byte[] ivs) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] finalIvs = new byte[16];
int len = ivs.length > 16 ? 16 : ivs.length;
System.arraycopy(ivs, 0, finalIvs, 0, len);
IvParameterSpec ivps = new IvParameterSpec(finalIvs);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivps);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] data, byte[] key, byte[] ivs) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] finalIvs = new byte[16];
int len = ivs.length > 16 ? 16 : ivs.length;
System.arraycopy(ivs, 0, finalIvs, 0, len);
IvParameterSpec ivps = new IvParameterSpec(finalIvs);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivps);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
iOS Code for Decryption:
- (NSData *) decryptFile:(NSString *)key withData:(NSData *)fileData {
char keyPtr[kCCKeySizeAES128+1];
bzero(keyPtr, sizeof(keyPtr));
NSString* iv = #"12345678";
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [fileData length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,keyPtr, kCCKeySizeAES128,
iv /* initialization vector (optional) */,
[fileData bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
free(buffer); //free the buffer;
return nil;
}
Give me any solution or suggestion for this problem
The issue is with the iv parameter.
1) You are passing a NSString* as iv. You probably want to pass in the actual bytes.
2) The length of iv should be 16 (in this case) as per the api docs of CCCrypt. See link below:
http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonCryptor.h

Categories

Resources