I am using the encryption class in Objective C for my iPhone app but I am struggling to get the same functionality working in JAVA from my android app. My encryption code is below:
NSString * _secret = #"password";
NSString * _key = #"1428324560542678";
StringEncryption *crypto = [[StringEncryption alloc] init];
NSData *_secretData = [_secret dataUsingEncoding:NSUTF8StringEncoding];
CCOptions padding = kCCOptionPKCS7Padding;
NSData *encryptedData = [crypto encrypt:_secretData key:[_key dataUsingEncoding:NSUTF8StringEncoding] padding:&padding];
I have tried to replicate it in JAVA but I get a different string when I encode the same data. So I am doing something wrong but I can't figure it out. Here is my JAVA code:
byte[] key = "1428324560542678".getBytes();
Cipher c = null;
try {
c = Cipher.getInstance("AES/ECB/PKCS7Padding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SecretKeySpec k = new SecretKeySpec(key, "AES");
try {
c.init(Cipher.ENCRYPT_MODE, k);
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
EditText tv1passwordText = (EditText) findViewById(R.id.password);
String password = URLEncoder.encode(tv1passwordText.getText().toString(), "UTF-8");
byte[] encryptedData = c.doFinal( password.getBytes());
Can anyone see where I am going wrong?
Based on the comments below I added getBytes but the strings produced are still different:
byte[] key = null;
try {
key = "1428324560542678".getBytes("UTF-8");
} catch (UnsupportedEncodingException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
Cipher c = null;
try {
c = Cipher.getInstance("AES/ECB/PKCS7Padding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SecretKeySpec k = new SecretKeySpec(key, "AES");
try {
c.init(Cipher.ENCRYPT_MODE, k);
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
EditText tv1passwordText = (EditText) findViewById(R.id.password);
byte[] password = tv1passwordText.getText().toString().getBytes("UTF-8");
byte[] encryptedData = c.doFinal(password);
Here is a sample of encryption and decryption:
public static SecretKey generateKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
return secret = new SecretKeySpec(password.getBytes(), "AES");
}
public static byte[] encryptMsg(String message, SecretKey secret) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
/* Encrypt the message. */
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8"));
return cipherText;
}
public static String decryptMsg(byte[] cipherText, SecretKey secret) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
/* Decrypt the message, given derived encContentValues and initialization vector. */
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
String decryptString = new String(cipher.doFinal(cipherText), "UTF-8");
return decryptString;
}
To encrypt:
SecretKey secret = EncUtil.generateKey();
EncUtil.encryptMsg(<String to Encrypt>, secret))
to decrypt
EncUtil.decryptMsg(<byte[]>, secret))
Instead of using ECB, you ought to use CBC or CTR if possible. ECB is insecure.
It looks like your Objective-C code is using UTF-8 encoding, but you're not specifying this in your Java code. Use getBytes("UTF-8").
One thing that I've noticed that has caused problems in the past is that the iOS string being encrypted is actually "Hello World\0", eg the string to be encrypted with an extra null at the end. so try adding a \0 to the end of your string in Java and see if it produces the same results.
Additionally, the URLEncoder step on java may be introducing extra control characters and other things that are not present on the iOS side. It may be worthwhile to compare the text after this with the text going into the iOS encryption step to ensure that they are exactly the same
Related
I am trying to decrypt the data in Java which was encrypted in PHP using AES-256-CBC. Decrypt method of Java cipher.doFinal throwing IllegalBlockSizeException. Can anyone help me to resolve this? Banging my head to fix this from the past 2 days. Please let me know if need more info.
public static String decrypt(String encryptedResult, String secretKey, String iv) {
String decrypted;
try {
byte[] bytes = new BigInteger(encryptedResult.trim(),16).toByteArray();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, makeKey(secretKey), makeIv(iv));
byte[] bytesFinal = cipher.doFinal(bytes);
decrypted = new String(bytesFinal);
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
static AlgorithmParameterSpec makeIv(String iv) {
try {
return new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
static Key makeKey(String secretKey) {
return new SecretKeySpec(secretKey.getBytes(), "AES");
}
Android code
String apiResponse = "EcUZvMif
Method:
protected void decryptDataWithAES(String apiResponse, String key) {
try {
es(StandardCharsets.UTF_8);
byte[] decodedResult = Base64.decode(apiResponse, Base64.NO_WRAP);
terSpec = new IvParameterSpec(first16ByteArray);
SecretKeySpec skey = new SecretKeySpec(byteArray, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(DECRYPT_MODE, skey, ivParameterSpec);
String decryptString = new String(cipher.doFinal(byteArray), StandardCharsets.UTF_8);
showLog("JSON: " + decryptString);
} catch (Exception e) {
e.printStackTrace();
}
}
Exception: javax.crypto.BadPaddingException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT
[wefopwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwefpwfpkpewfpkoewfkowf
ewfwefwef]bhdfuiyh
You are trying to decrypt the "key", I think you need to decrypt the apiResponse
Also you need the exact same IV the message was encrypted with, otherwise you won't be able to decrypt
Here is a static method to decrypt using AES with secretKey
private final static String AES_PADDING = "AES/ECB/PKCS5PADDING"; //this need to be same as DECRYPTION
private String secretKey = "Your secret key"; //your secret key
//DecryptString
#SuppressLint("GetInstance")
public static String AESDecryptionString(String encryptedStringData) {
Cipher decipher = null;
byte[] encryptedString = encryptedStringData.getBytes(StandardCharsets.ISO_8859_1);
String returnData = encryptedStringData;
try {
decipher = Cipher.getInstance(AES_PADDING);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
byte[] decryption;
try {
assert decipher != null;
decipher.init(Cipher.DECRYPT_MODE, secretKey);
decryption = decipher.doFinal(encryptedString);
returnData = new String(decryption);
} catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
return returnData;
}
You can also use my library to encrypt/decrypt string using AES
When I encrypt the password !QAZxdr5 by the code below:
public static String encryptPassword(String msg) {
try {
KeySpec keySpec = new DESKeySpec(msg.getBytes());
SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(keySpec);
Cipher ecipher = Cipher.getInstance(key.getAlgorithm());
ecipher.init(Cipher.ENCRYPT_MODE, key);
//Encode the string into bytes using utf-8
byte[] utf8 = msg.getBytes("UTF8");
//Encrypt
byte[] enc = ecipher.doFinal(utf8);
//Encode bytes to base64 to get a string
return new String(Base64.getEncoder().encode(enc));
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
I got the output: QQiu2a4NT9YfDAtmHjbk1A==
Now I try to create the decryption for this:
public static String decrypt(String msg) {
try {
KeySpec keySpec = new DESKeySpec(msg.getBytes());
SecretKey key =
SecretKeyFactory.getInstance("DES").generateSecret(keySpec);
Cipher decipher = Cipher.getInstance(key.getAlgorithm());
decipher.init(Cipher.DECRYPT_MODE, key);
// Decode base64 to get bytes
byte[] dec = Base64.getDecoder().decode(msg.getBytes("UTF-8"));
//Decrypt
byte[] utf8 = decipher.doFinal(dec);
//Decode using utf-8
return new String(utf8, "UTF8");
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
However, it doesn't work properly as it returned null :(. Can you please help to check where I'm wrong?
Normally, Base64 encoding of !QAZxdr5 is IVFBWnhkcjU=, however, your code uses a key to encode extra AFAI understand, that's why you get QQiu2a4NT9YfDAtmHjbk1A==. So, decrypt() method needs to know also the key generated already, nonetheless, yours wouldn't.
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Base64;
class EncryptDecryptTest {
public static void main(String[] args) throws Exception {
String key = "it's our key ~~~where is my +1 karma ??~~~ - soner";
String ciphertext = encrypt(key, "!QAZxdr5");
String decrypted = decrypt(key, ciphertext.trim());
String encrypted = encrypt(key, decrypted.trim());
if (ciphertext.contentEquals(encrypted.trim())) {
System.out.println("decrypted!");
} else {
System.out.println("wrong key!");
}
}
public static String encrypt(String key, String data)
throws GeneralSecurityException {
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(dataBytes));
}
public static String decrypt(String key, String data)
throws GeneralSecurityException {
byte[] dataBytes = Base64.getDecoder().decode(data);
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] dataBytesDecrypted = (cipher.doFinal(dataBytes));
return new String(dataBytesDecrypted);
}
}
As a side note,
The fact that return new String(Base64.getEncoder().encode(enc)); uses toString() method of array
should be return Base64.getEncoder().encodeToString(enc); in order to get expected encoded datum.
In DES encryption, it uses the same key to encrypt and decrypt a message.
So for both operations you need to have the same key.
In your case, you have used the same string as the key and as the password.
public static String encryptPassword(String msg) {
try {
KeySpec keySpec = new DESKeySpec(msg.getBytes());
In above code segment when creating new DESKeySpec object, you need to pass the key as well.
public static String decrypt(String msg) {
try {
KeySpec keySpec = new DESKeySpec(msg.getBytes());
Even in above decypt method you have to pass the same key you have used in encrypt method.
But in this you have given the encoded string to generate the key.
That is where you have gone wrong.
So i suggest you to change the methods parameters by adding one more parameter as key and then pass the same value for key in both methods.
public static String encryptPassword(String msg, String keySp) {
try {
KeySpec keySpec = new DESKeySpec(keySp.getBytes());
}
public static String decrypt(String msg, String keySp) {
try {
KeySpec keySpec = new DESKeySpec(keySp.getBytes());
}
I have included only the lines that needed to be changed.
You can call those methods by,
String key = "!QAZxdr5";
String password = "!QAZxdr5";
String encriptedPassword = encryptPassword(password, key);
System.out.println(decrypt(encriptedPassword, key));
Am using library https://github.com/fukata/AES-256-CBC-Example for Encryption and decryption And with the help of Issue with key and iv on AES 256-CBC. Decryption is working properly.
But in the Case of Encryption this error happens
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.vinu.aessamble/com.example.vinu.aessamble.MainActivity}: java.lang.RuntimeException: javax.crypto.IllegalBlockSizeException:error:1e06c06a:Cipher functions:EVP_EncryptFinal_ex:DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
And i am using
String encrypted = AESUtil.encrypt("My PlainText to encrypt");
In AESUtils
public static String encrypt(String src) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, makeKey(), makeIv());
return Base64.encodeBytes(cipher.doFinal(src.getBytes()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Using this for PKCS5Padding encryption is working but can't decrypted using the method. When using NoPadding for encryption it shows above error.
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
byte[] array= Base64.decode(src);
byte[] ivData=Arrays.copyOfRange(array,0,16);
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
// Init the cipher with decrypt mode, key, and IV bytes array (no more hardcoded)
cipher.init(Cipher.DECRYPT_MODE, makeKey(), new IvParameterSpec(ivData));
// Decrypt same old way
decrypted = new String(cipher.doFinal(encrypted));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
makeIv()
static AlgorithmParameterSpec makeIv() {
try {
return new IvParameterSpec(sha256digest16(ENCRYPTION_IV));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
makeKey()
static Key makeKey() {
try {
byte[] key = ENCRYPTION_KEY.getBytes("UTF-8");
return new SecretKeySpec(key, "AES");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
Iv and key are
private static final String ENCRYPTION_KEY = qwertyuiopasdfghjklzxcvbnmqwerty";
private static final String ENCRYPTION_IV = "qbmocwtttkttpqvv";
Help me to do encryption.
I have the following Java code which correctly decrypts a base64 encoded payload:
private static byte[] decryptPBKDF2WithBC(char[] password, byte[] data, byte[] salt, byte[] iv)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password), salt,1024);
KeyParameter params = (KeyParameter)generator.generateDerivedParameters(256);
byte[] endcoded = params.getKey();
SecretKey key = new SecretKeySpec(endcoded, "AES");
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return ciph.doFinal(data);
}
public String decrypt(String encrypted){
String salt = SALT;
String password = PASSWORD;
String[] parts = encrypted.split("--");
if (parts.length != 2) return null;
byte[] encryptedData = Base64.decode(parts[0], Base64.DEFAULT);
byte[] iv = Base64.decode(parts[1], Base64.DEFAULT);
byte[] result = null;
try {
result = decryptPBKDF2WithBC(password.toCharArray(), encryptedData, salt.getBytes(), iv);
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
catch(Exception e){
e.printStackTrace();
}
try {
return new String(result, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
catch(Exception e){
e.printStackTrace();
return null;
}
}
The problem I'm having is that I need to port this to IOS. I've searched for several examples online, but I haven't been able to find one that uses a password, a salt and an IV. Are there any security/IOS experts who know enough about the IOS crypto libraries to at least point me in the right direction (which libraries to use or any code samples)?
Use RNCryptor if possible.
On iOS use the CommonCrypto library for the decryption along with the NSData Base64 methods.
You need to add Security.framework and you can look at the header files for the methods and more information.