I have a file that has been encrypted on Android using this code:
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESUtils {
private static final String IV_STRING = "123456789876543";
private String key = "mysecretkey12345";
public static byte[] encryptData(String key, byte[] byteContent) {
byte[] encryptedBytes = null;
try {
byte[] enCodeFormat = key.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
encryptedBytes = cipher.doFinal(byteContent);
} catch (Exception e) {
e.printStackTrace();
}
return encryptedBytes;
}
public static byte[] decryptData(String key, byte[] encryptedBytes) {
byte[] result = null ;
try {
byte[] sEnCodeFormat = key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(sEnCodeFormat, "AES");
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
result = cipher.doFinal(encryptedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
I tried to reverse engineer the decryption in Swift using CommonCrypto like this:
import CommonCrypto
let keyStr:String = "mysecretkey12345"
let ivStr:String = "123456789876543"
func aesDecrypt(data:NSData) -> Data? {
let k:NSData = keyStr.data(using: .utf8)! as NSData
let dbytes = data.bytes
let kbytes=k.bytes
if let keyData = keyStr.data(using: .utf8),
let cryptData = NSMutableData(length: Int((data.length)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
kbytes, keyLength,
ivStr,
dbytes, data.length,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
return (cryptData.copy() as! Data)
}
else {
return nil
}
}
return nil
}
I am quite new to encryption, but from my research I found that CC uses CBC by default and PKCS7Padding is basically identical to PKCS5Padding. However, the decryption does not deliver the results that I expect! The swift code was frankensteined together from various sources, including many solutions suggested here on stackoverflow. Main problem is that most examples use key and iv as Data, whereas I have strings - not sure my conversion causes problems. Secondly, many simply convert string messages, whereas I convert data (from files) directly - should not affect it too much, actually makes the code simpler avoiding data->string conversion. But since it doesn't work, what did I miss?
Okay, I think I figured the problem. Obviously, there is a problem with NSData.bytes and one should work withUnsafeBytes. Furthermore, my issue may was that the IV was not part of the data, as many examples assumed, so I missed 16 bytes when decrypting. The following code works for me, hope it will help someone!
func decrypt(data: Data) -> Data {
let key: Data = keyStr.data(using: .utf8) ?? Data()
let iv: Data = ivStr.data(using: .utf8) ?? Data()
if(keyStr.count == kCCKeySizeAES128){print("Key OKAY")} else {print("Key NOT okay")}
if(ivStr.count == kCCBlockSizeAES128){print("IV OKAY")} else {print("IV NOT okay")}
var buffer = Data(count: data.count)
var numberBytesDecrypted: Int = 0
let cryptStatus: CCCryptorStatus = key.withUnsafeBytes {keyBytes in
data.withUnsafeBytes {dataBytes in
buffer.withUnsafeMutableBytes {bufferBytes in iv.withUnsafeBytes {ivBytes in
CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCDecrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES128), // alg: CCAlgorithm
CCOptions(kCCOptionPKCS7Padding), // options: CCOptions
keyBytes.baseAddress, // key: the "password"
key.count, // keyLength: the "password" size
ivBytes.baseAddress, // iv: Initialization Vector
dataBytes.baseAddress!, // dataIn: Data to decrypt bytes
data.count, // dataInLength: Data to decrypt size
bufferBytes.baseAddress, // dataOut: decrypted Data buffer
data.count, // dataOutAvailable: decrypted Data buffer size
&numberBytesDecrypted // dataOutMoved: the number of bytes written
)}
}
}
}
if(cryptStatus == CCCryptorStatus(kCCSuccess)){
return buffer[..<numberBytesDecrypted]
} else {
print("Decryption failed")
return data
}
}