I'm using AES Decryption on my Android Project to decrypt large string objects ( > 1 MB ).
I'm using this method :
public static String decryptAES(String cryptedString, byte[] byteArrayAESKey) {
try {
IvParameterSpec ips = new IvParameterSpec(General.InitVector.getBytes("UTF-8"));
SecretKey aesKey = new SecretKeySpec(byteArrayAESKey, "AES");
byte[] TBCrypt = Base64.decode(cryptedString, Base64.DEFAULT);
// Decryption cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
// Initialize PBE Cipher with key and parameters
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ips);
// Decrypt the cleartext
byte[] deciphertext = decryptCipher.doFinal(TBCrypt); // this may take a long time depending on string input length
return new String(deciphertext, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
Log.e("AES", "Decrypt failed : " + e.getMessage());
return "";
}
}
It works well, but on large encrypted strings, it takes a long time on many devices.
Is there a way to improve this method on android devices ? Should I cut the encrypted string to accelerate the process ? Should I use SpongyCastle ?
byte[] deciphertext = decryptCipher.doFinal(TBCrypt); Dont do that! Instead consider using streams, maybe directly to output file stream (if needed)?.
Is there a way to improve this method on android devices ?
Maybe, you could take a look here , and there's saying that the AES is pretty fast, though.
Should I cut the encrypted string to accelerate the process ?
Yes, this should be the problem. Usually you only have to encrypt the critical parts of the data. Maybe a refactor should resolve the question.
Should I use SpongyCastle ?
Don't know, but if i where you i would first look at the data encrypted.
Related
I give up in trying to research for solutions regarding my problem. I've done my part and search about this problem and encountered solutions (like this https://stackoverflow.com/a/21252990/5328303) which was really the same problem as mine but he is using aes-128-ecb.
I cannot get the solution to work for aes-192-ecb mode.
Here's the node.js part (take note I cannot change this part of the code since this is a third party provider and I'm very limited.)
console.log(encrypt("hello world"))
function encrypt(data) {
const aesKey = '4327601417486622'
const algorithm = 'aes-192-ecb'
const cipher = crypto.createCipher(algorithm, aesKey)
const crypted = cipher.update(data, 'utf-8', "hex") + cipher.final("hex")
return crypted
}
// expected: 066c47b162cd5c464ea9805742c1af9b
And here's my Java function:
public static String decrypt(String seed, String encrypted) throws Exception {
byte[] keyb = seed.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(keyb);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
Cipher dcipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, skey);
byte[] clearbyte = dcipher.doFinal(toByte(encrypted));
return new String(clearbyte);
}
The java code above works well if I use aes-128-ecb on my node code but it cannot decode when I'm using aes-192-ecb or even aes-256-ecb.
Maybe I just don't quite understand openssl EVP_BytesToKey function since I read that crypto.createCipher() uses it when encrypting. It also said that it is hashing the key with MD5 which I'm currently doing with the java code.
Also I was thinking that the aesKey that I have is only 16 bytes and maybe that's why it won't work with AES-192 and only works with AES-128. I want to understand how openssl/crypto does it when I'm only passing a 16 byte key with the required 24 bytes key for AES-192 since I cannot change the node.js code.
Am I on the right track? Can anyone guide me?
Thank you!
I am encrypting my message with a constant secret key and random IV using AES/CTR/NoPadding in Java.
I plan to prepend the IV to the cipher text and then Base64 encode it.
Now, there is one problem that is bothering me. There can be multiple IV + Cipher text combinations that can result in my original message. Isn't that a problem? Also, is it safe to send IV as such (i.e. prepend/append to the cipher text) or there is some procedure that I should follow?
I am relatively new to cryptography, so pardon me if it's very simple question. I couldn't find any satisfying answer to this.
EDIT:
public static String encrypt(String message) {
try {
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
byte[] iv = generateRandomIV();
cipher.init(Cipher.ENCRYPT_MODE, SECRET_KEY, new IvParameterSpec(iv));
byte[] cipherText = cipher.doFinal(message.getBytes("utf-8"));
return DatatypeConverter.printBase64Binary(concat(iv, cipherText));
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String encryptedMessage) {
try {
byte[] bytes = DatatypeConverter.parseBase64Binary(encryptedMessage);
byte[] iv = getIV(bytes);
byte[] cipherText = getCipherText(bytes);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY, new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText));
} catch (Exception ex) {
ex.printStackTrace();
return encryptedMessage;
}
}
private static byte[] getIV(byte[] bytes) {
return Arrays.copyOfRange(bytes, 0, 16);
}
private static byte[] getCipherText(byte[] bytes) {
return Arrays.copyOfRange(bytes, 16, bytes.length);
}
and then
public static void main(String[] args) {
System.out.println(decrypt("wVroKV1UnL2NXiImS83hLKpLLJKk"));
System.out.println(decrypt("Q0tWAMZDhqMo0LbtEY7lF9D8Dkor"));
}
Both these produce same output -- "goody"
There can be multiple IV + Cipher text combinations that can result in my original message. Isn't that a problem?
The reason that you're using different IV's is that you can send the same message twice using the same key, with different ciphertext.
If it would generate the same ciphertext an adversary would know that the same message was send, leaking information about the message. So the idea of the IV is that it generates a different ciphertext and in most cases that is beneficial rather than a problem.
If it is a problem depends on your protocol. Note that the ciphertext length may still show information about the plaintext (i.e. "Affirmative, Sergeant" will of course be different than the encryption of "No").
You'll need an authentication tag / MAC value to protect against change of the message. Furthermore, you may want to include something like a message sequence number to make sure that replay attacks don't happen.
However, the deeper you go the more complex encryption becomes. If you require secure transport then in the end it is infinitely easier to use TLS or SSH (or any other applicable transport layer security).
Also, is it safe to send IV as such (i.e. prepend/append to the cipher text) or there is some procedure that I should follow?
Prepending it ("prepend" is not a word in many dictionaries, you could use "prefix" as well) is a common way of handling the IV, yes. The IV may be sent anyway and it doesn't need to be kept confidential. In the case of CBC however it must be random, not a sequence number.
So i have an android app, and a google app engine server written in python.
The android app needs to send some sensible information to the server, and the way I do that is by doing an http post.
Now i have been thinking about encrypting the data in android before sending it, and decrypting it once it is on the gae server.
This is how i encrypt and decrypt in java :
private static final String ALGO = "AES";
public static String encrypt(String Data) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(Data.getBytes());
// String encryptedValue = new BASE64Encoder().encode(encVal);
byte[] decoded = Base64.encodeBase64(encVal);
return (new String(decoded, "UTF-8") + "\n");
}
public static String decrypt(String encryptedData) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue =Base64.decodeBase64(encryptedData);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
private static Key generateKey() throws Exception {
Key key = new SecretKeySpec(Constant.keyValue, ALGO);
return key;
}
And this is how i try to decrypt on the server (i don't know yet how to do the encryption..maybe you guys can help with that too)
def decrypt(value):
key = b'1234567891234567'
cipher = AES.new(key, AES.MODE_ECB)
msg = cipher.decrypt(value)
return msg
As i looked in the logs, the string test that i get is : xVF79DzOplxBTMCwAx+hoeDJhyhifPZEoACQJcFhrXA= and because it is not a multiple of 16 (idk why, i guess this is because of the java encryption) i get the error
ValueError: Input strings must be a multiple of 16 in lenght
What am i doing wrong?
Why are you not using ssl (aka https)? That should provide all the encryption needed to transport data securely and privately between the phone and App Engine.
The basics of it: Instead of sending data to http://yourapp.appspot.com/, send it to https://yourapp.appspot.com/.
For a complete secure and authenticated channel between App Engine and Android, you can use Google Cloud Endpoints. It will even generate the Android side code to call it.
Java:
https://developers.google.com/appengine/docs/java/endpoints/
https://developers.google.com/appengine/docs/java/endpoints/consume_android
Python:
https://developers.google.com/appengine/docs/python/endpoints/
https://developers.google.com/appengine/docs/python/endpoints/consume_android
For a longer show and tell, check the IO 13 talk: https://www.youtube.com/watch?v=v5u_Owtbfew
This string "xVF79DzOplxBTMCwAx+hoeDJhyhifPZEoACQJcFhrXA=" is a base64-encoded value.
https://en.wikipedia.org/wiki/Base64
Base64 encoding is widely used lots of applications, it's a good way to encode binary data into text. If you're looking at a long encoded value, the "=" at the end can be a good indicator of base64 encoding.
In your python code you probably need to base64 decode the data before handing it to the decryption function.
I have two recommendations:
If crypto isn't a comfort zone for you, consult with someone who is good in this area for your project.
Be aware that embedding a symmetric encryption key in an Android app that you distribute is a bad idea. Anyone that can get a copy of your app can extract that key and use it to decrypt or spoof your messages.
I have an app with java and PHP files. The java files send content to the PHP files, and this one send the response to the java file, by HTTP everything. I have the response with JSON format.
I would like to encrypt the information and decode it in the other side, java->php and php->java(this is the most important) but I don't know how to do it.
Edit:
I am trying BLOWFISH, here is my code in PHP(crypt the data and send to Java) and Java(get the data and decode it)
PHP
$key = "this is the key";
$crypttext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $result_json, MCRYPT_MODE_ECB);
echo($crypttext);
JAVA
public String decryptBlowfish(String to_decrypt, String strkey) {
System.out.println(to_decrypt);
try {
SecretKeySpec key = new SecretKeySpec(strkey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(to_decrypt.getBytes());
return new String(decrypted);
} catch (Exception e) {
System.out.println(e.getMessage());
;
return null;
}
}
System.out.println(decryptBlowfish(result, "this is the key"));
The result when I execute is:
Input length must be multiple of 8 when encrypting with padded cipher
or sometimes
Given final block not properly padded
Agreed with the comment that's what SSL is for see here for a client java application that uses SSL Certificate and encryption to connect to an HTTPS/SSL site: http://www.mkyong.com/java/java-https-client-httpsurlconnection-example/ next you might want to have an HTTPS/SSL php server this should help: http://cweiske.de/tagebuch/ssl-client-certificates.htm Or use this Opensource library: http://nanoweb.si.kz/
If the above fails then I don't know, but a last resort would be writing your own, you may never know how secure it really is?
You might want to use the same algorithm for decoding/decrypting namely "blowfish/ecb/nopadding" instead of "blowfish".
private static final String DECRYPTION_ALGORITHM = "blowfish/ecb/nopadding";
private static final String KEY_ALGORITHM = "blowfish";
private static byte[] decrypt(byte[] keyData, byte[] valueData) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(keyData, KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance(DECRYPTION_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
return cipher.doFinal(valueData);
}
If you don't want SSL, which I recommend too, you can try this:
$str = 'hello world'; //your input data
$pass = 'haj83kdj843j'; //something random, the longer the better
$l = strlen($pass);
for ($i=0; $i<strlen($str); $i++)
{
$str[$i] = chr(ord($str[$i]) + ord($pass[$i % $l]));
}
It is fast and easy to write a coder/encoder in any language you want. The resulting string is a binary string so you might want to convert it using base64_encode or something. Should give quite good security.
I am trying to encrypt and decrypt a message as mentioned in the below code. Basically I want to encrypt a message with a public key and convert that encrypted message from byte array to String. And decrypt this string into original text. Here are the both methods. Here encryption works fine but decryption fails (error is "Data must start with zero"). I think this is causing because I convert encrypted byte array into String.
How do I solve this? (I want to have encrypted byte array as string and use it for decryption) Is there any other approach (with public and private keys)
public static String getEncryptedMessage(String publicKeyFilePath,
String plainMessage) {
byte[] encryptedBytes;
try {
Cipher cipher = Cipher.getInstance("RSA");
byte[] publicKeyContentsAsByteArray = getBytesFromFile(publicKeyFilePath);
PublicKey publicKey = getPublicKey(publicKeyContentsAsByteArray);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(plainMessage.getBytes());
return new String(encryptedBytes);
} catch (Throwable t) {
}
}
public static String getDecryptedMessage(
String privateKeyFilePath, String encryptedMessage)
{
byte[] decryptedMessage;
try {
Cipher cipher = Cipher.getInstance("RSA");
byte[] privateKeyContentsAsByteArray = getBytesFromFile(privateKeyFilePath);
PrivateKey privateKey = getPrivateKey(privateKeyContentsAsByteArray);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
decryptedMessage = cipher.doFinal(encryptedMessage.getBytes());
return new String(decryptedMessage);
} catch (Throwable t) {
}
If you look at this page (http://www.wikijava.org/wiki/Secret_Key_Cryptography_Tutorial) you will need to do base-64 encoding to turn the bytes into a string, then to decrypt it you would just decode it then decrypt.
Base-64 encoding uses the first 7 bits of a byte, to make something that is printable or emailable, for example.
UPDATE:
I made a mistake, there are 64 characters that it would be encoded in, again, in order to make it easier to use as something printable.
Why don't you treat the message as byte array from encryption to decryption? Why changing it to String in the middle? (I know it seems like a question, but it's actually an answer...)
Using RSA directly on unformatted data may leave your application vulnerable to an adaptive chosen ciphertext attack. For details please see Chapter 8, pages 288-289, of the Handbook of Applied Cryptography, a freely-available book from CRC Press. (It's well worth buying the bound edition, if you're really interested in cryptography -- you'll be stunned at the quality for the price.)
Because of this attack, most protocols that integrate RSA use RSA for encrypting randomly-generated session keys or signing hash functions with outputs that ought to be indistinguishable from random, OR using very carefully formatted messages that will fail to be correctly interpreted. (See Note 8.63 in HAC for details.)