How to encrypt in Java and decrypt in Android and iOS - java

I have a Linux server running a Java-jar file that encrypts several files.
The Android and iPhone App download that file and shall decrypt it. What algorithm I have to use to do so?
I recognized that the algorithms I used in Java do not work in Android. What I did in Java was:
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
what didn't work in above code?
Any alternatives?

iOS:
I use NSString+AESCrypt (https://github.com/Gurpartap/AESCrypt-ObjC)
Sample:
NSString* encrypted = [plainText AES256EncryptWithKey:#"MyEncryptionKey"];
NSString* decrypted = [encrypted AES256DecryptWithKey:#"MyEncryptionKey"];
Android (AES256Cipher - https://gist.github.com/dealforest/1949873):
Encrypt:
String base64Text="";
try {
String key = "MyEncryptionKey";
byte[] keyBytes = key.getBytes("UTF-8");
byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] cipherData;
//############## Request(crypt) ##############
cipherData = AES256Cipher.encrypt(ivBytes, keyBytes, passval1.getBytes("UTF-8"));
base64Text = Base64.encodeToString(cipherData, Base64.DEFAULT);
}
catch ( Exception e ) {
e.printStackTrace();
}
Decrypt:
String base64Text="";
String plainText="";
try {
String key = "MyEncryptionKey";
byte[] keyBytes = key.getBytes("UTF-8");
byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] cipherData;
//############## Response(decrypt) ##############
base64Text = User.__currentUser.getPasscode();
cipherData = AES256Cipher.decrypt(ivBytes, keyBytes, Base64.decode(base64Text.getBytes("UTF-8"), Base64.DEFAULT));
plainText = new String(cipherData, "UTF-8");
}
catch ( Exception e )
{
e.printStackTrace();
}

The link below gives a nice example of encryption and decryption using Symmetric key encryption.
The symmetric key used is a custom plain text.
This helps if we need to to decrypt using a IOS device.
The example uses a AES 128 bit encryption. Note that it uses IV parameters.
Since the encryption is 128 bit the length of the key should be 16.
On Android side the same method implementations can be used since the language is Java. In IOS CommonCryptor.h can be used for encryption decryption.
http://www.java-redefined.com/2015/06/symmetric-key-encryption-ios-java.html

Related

How to use SecureRandom instead of using hardcoded bytes array for Java AES Encrytion and Decryption?

In my code I am using hardcoded arrays(given below) for IV and key
**private static byte[] IVAes = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
private static byte[] keyAes = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
public static String encryptAes(String strPlain) {
byte[] encrypted = null;
if (StringUtils.isBlank(strPlain)) {
return strPlain;
}
byte[] toEncrypt = strPlain.getBytes();
try {
AlgorithmParameterSpec paramSpec = new IvParameterSpec(IVAes);
// Generate the key specs.
SecretKeySpec skeySpec = new SecretKeySpec(keyAes, AES_ALGORITHM);
// Instantiate the cipher
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, paramSpec);
encrypted = cipher.doFinal(toEncrypt);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
LOGGER.error(e.getMessage(), e);
}
return new String(Base64.encodeBase64(encrypted));
}**
but using hardcoded array as IV and Key is not prefered due to security perspective. Instead of this type of Hardcoded array can I use SecureRandom() as given below-
**public static String encryptAes(String strPlain) {
byte[] encrypted = null;
if (StringUtils.isBlank(strPlain)) {
return strPlain;
}
byte[] toEncrypt = strPlain.getBytes();
try {
//---------calling generateIV method
AlgorithmParameterSpec paramSpec = generateIv();
// Instantiate the cipher
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, paramSpec);
encrypted = cipher.doFinal(toEncrypt);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
LOGGER.error(e.getMessage(), e);
}
return new String(Base64.encodeBase64(encrypted));
}
public static IvParameterSpec generateIv() {
byte[] IVAes = new byte[16];
new SecureRandom().nextBytes(IVAes);
return new IvParameterSpec(IVAes);
}
int n = 128;
public static SecretKey generateKey(int n) throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(n);
SecretKey key = keyGenerator.generateKey();
return key;
}**
I just wanted to know that creating array of 16 bytes for IV and key by using SecureRandom and also key generator will give same result as it was giving when I use hardcoded array as shown above??
The whole idea of a cipher is that it is indistinguishable from random (not considering the length of the ciphertext, that obviously depends on the size of the message in some way). If you have a unique key and IV then the ciphertext should be indistinguishable from random, although for CBC the IV should also be randomized. If you want to test if it "works" then you'll have to decrypt the ciphertext. Otherwise you can only check if the ciphertext size is about right.
However, if you are asking about the security of your code then sure, the use of SecureRandom and KeyGenerator seems fine. You might want to prefix the IV to the ciphertext, that's the common way to communicate a random IV. If you want to know how to distribute keys then I'd suggest reading into symmetric key management; that depends on the use case.
For my fellow crypto nerds, yes, sure related key attacks are a thing so having a unique key & IV is not entirely the whole story, but these keys are randomized, so that's fine. There are probably some other theoretical / practical issues with the answer which can usually be ignored.

How to have a constant initialization vector and secret key in java?

I'm trying to decrypt an access token (it's a String), which is used to default access an Dropbox account and uploading files into it. So right now, I always need that access token to make file uploadings.
Until now, I've been generating a new initialization vector (IV) and a new secret key to encrypt and decrypt the access token. However, I want to store these two in the source code, as constant variables/attributes. The reason why I want them to remain the same ? Because I will give a crypted access token (always the same encoded one) to the users, and the app should keep the IV and the secret key inside the source code.
How can I store them in my source code ?
I tried to write the string values of the IV and of the secret key in files. I use the string from the files, and I assign the string values to string constants in my code. Then i use my constants to create byte arrays for converting into the IV and into the secret key. I'm not sure if this will work yet, it's still in development.
You'd better heed the advice. Storing the key is bad but can sometimes be defended if no other options are available. There is however generally no reason to use a static IV. You can just prefix the IV (which is 16 bytes for most modes of operation) to the ciphertext instead.
Anyway, to store them as static values, just take a look at the following code; note that you should generate them as random values in advance, not the static values you're seeing here:
private static final byte[] KEY_DATA = {
(byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
(byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
(byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B,
(byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F,
};
private static final byte[] IV_DATA = {
(byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
(byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
(byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B,
(byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F,
};
public static void main(String[] args) throws Exception {
Cipher aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey key = new SecretKeySpec(KEY_DATA, "aes");
IvParameterSpec iv = new IvParameterSpec(IV_DATA);
aes.init(Cipher.ENCRYPT_MODE, key, iv);
...
}
Note that SecretKeySpec implements the interface SecretKey for easy usage.

AES encryption in CBC mode with an inline IV

I would like to implement this code which is, AES encryption in CBC mode with an inline IV, but there is this error message:
Wrong IV length: must be 16 bytes long
and the code is:
package implementaes;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Aesaesaes
{
public static void main(String[] args)
{
try
{
//Lookup a key generator for the AES cipher
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecretKey key = kg.generateKey();
SecretKeySpec keySpec = new
SecretKeySpec(key.getEncoded(), "AES");
//Lookup an instance of a AES cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//initialize IV manually
byte[] ivBytes = new byte[] {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//create IvParameterSpecobject
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
//Initialize the cipher using the secter key
cipher.init(Cipher.ENCRYPT_MODE, keySpec,ivSpec);
String plainText = "This is a secret!";
byte[] cipherText = cipher.doFinal(plainText.getBytes());
System.out.println("Resulting Cipher Text:\n");
for(int i=0;i<cipherText.length;i++)
{
System.out.print(cipherText[i] + " ");
}
System.out.println("");
} catch (Exception e)
{
e.printStackTrace();
}
}
}
How can I sort it out? By the way I tried:
byte[] ivBytes = new byte[] {0x00,0x00,0x00,0x00};
to be 16 Byte, but does not work
The ivBytes that you are defining is currently 8 bytes:
byte[] ivBytes = new byte[] {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
Each member in the ivBytes array represents one byte. You need an array of 16 entries:
byte[] ivBytes = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
UPDATE
I thought it was obvious that you were going to supply your own values to the IV but it's probably worth it to point you to Dave's comment that it's in your best interest to not initialize the IV to all zeros. See how to pick an IV

javax.crypto.Cipher working differently since Android 6 Marshmallow

I've been successfully using javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding") to Authenticate with DESFire cards on Android (following the example here: https://stackoverflow.com/a/14160507/2095694). It's been working on several devices from Android 4 to 5, but stopped working on my Nexus 7 updated to 6 Marshmallow (and 6.0.1). It had been working on the same device before updating.
It seems Cipher is working differently, giving different results for the same key and data. Running the following code...
public static void testCipher() throws Exception
{
byte[] KEY =
new byte[]{
(byte) 0x0C, (byte) 0x09, (byte) 0x03, (byte) 0x0E,
(byte) 0x05, (byte) 0x0A, (byte) 0x0D, (byte) 0x02,
(byte) 0x03, (byte) 0x0A, (byte) 0x09, (byte) 0x0B,
(byte) 0x06, (byte) 0x10, (byte) 0x04, (byte) 0x10
};
byte[] DATA =
new byte[]{
(byte) 0x29, (byte) 0xDA, (byte) 0xC0, (byte) 0xC4,
(byte) 0xB8, (byte) 0x47, (byte) 0x13, (byte) 0xA2};
byte[] newByte8 = new byte[8]; //Zeroes
android.util.Log.d("TEST", "KEY : " + bin2hex(KEY));
android.util.Log.d("TEST", "DATA: " + bin2hex(DATA));
android.util.Log.d("TEST", "IVPS: " + bin2hex(newByte8));
android.util.Log.d("TEST", "----");
javax.crypto.Cipher cipher =
javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding");
cipher.init(
Cipher.DECRYPT_MODE,
new javax.crypto.spec.SecretKeySpec(KEY, "DESede"),
new javax.crypto.spec.IvParameterSpec(newByte8));
byte[] result = cipher.doFinal(DATA);
android.util.Log.d("TEST", "RSLT: " + bin2hex(result));
}
public static String bin2hex(byte[] data) {
return String.format("%0" + (data.length * 2) + "X", new java.math.BigInteger(1, data));
}
... gives me the following output:
KEY : 0C09030E050A0D02030A090B06100410
DATA: 29DAC0C4B84713A2
IVPS: 0000000000000000
----
RSLT: 47BC415065B8155E
Normal value, what it should be, always worked and card ends up authenticating correctly, so it's doing it the way the card expects. As a said I tried on several devices (Android 4 and 5) and they give the same result.
But on my Nexus 7 now with Marshmallow I get something else (and the authentication ends up failing)
RSLT: F3ADA5969FA9369C
Has something changed in the libraries?
It seems they changed the default provider in Marshmallow.
A simple:
cipher.getProvider().getName();
Shows "AndroidOpenSSL" for Marshmallow, where it was "BC" (BouncyCastle I suppose) before.
Using the other getInstance overload...
javax.crypto.Cipher cipher =
javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding","BC");
...gives me the expected result on my Nexus with Marshmallow.
Update: I now get this warning:
The BC provider is deprecated and when targetSdkVersion is moved to P this method will throw a NoSuchAlgorithmException. To fix this you should stop specifying a provider and use the default implementation
Cipher#getInstance should not be called with ECB as the cipher mode or without setting the cipher mode because the default mode on android is ECB, which is insecure.
So I have ended up using the other answer here that will (hopefully) work on all versions of Android.
there is a android bug issued:
https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=triple%20des&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened&groupby=&sort=&id=189292
you can also solve your problem by changing you key to 24 bytes len as below:
MessageDigest md = MessageDigest.getInstance("MD5");
seed_key = md.digest(new String(key).getBytes());
if (seed_key.length == 16) {
byte[] tempkey = new byte[24];
System.arraycopy(seed_key, 0, tempkey, 0, 16);
System.arraycopy(seed_key, 0, tempkey, 16, 8);
seed_key = tempkey;
}
SecretKeySpec keySpec = new SecretKeySpec(seed_key, "DESede");
nCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
byte[] IVector = new byte[] { 27, 9, 45, 27, 0, 72, (byte) 171, 54 };
IvParameterSpec iv = new IvParameterSpec(IVector);
nCipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] cipherbyte = nCipher.doFinal(data.getBytes());
encodeTxt = new String(Base64.encodeBase64(cipherbyte));

Java:AES Encryption using CTR mode; unable to decrypte

I am using following code but It's not decrypting the text properly, what am I getting as output is
ciphered: %öNo2F?¢¶SHºûÅ“?¾
plaintext: hello × am originÎl
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
// Dernier exemple CTR mode
// Clé 16 bits
byte[] keyBytes = new byte[] { (byte) 0x36, (byte) 0xf1, (byte) 0x83,
(byte) 0x57, (byte) 0xbe, (byte) 0x4d, (byte) 0xbd,
(byte) 0x77, (byte) 0xf0, (byte) 0x50, (byte) 0x51,
(byte) 0x5c, 0x73, (byte) 0xfc, (byte) 0xf9, (byte) 0xf2 };
// IV 16 bits (préfixe du cipherText)
byte[] ivBytes = new byte[] { (byte) 0x69, (byte) 0xdd, (byte) 0xa8,
(byte) 0x45, (byte) 0x5c, (byte) 0x7d, (byte) 0xd4,
(byte) 0x25, (byte) 0x4b, (byte) 0xf3, (byte) 0x53,
(byte) 0xb7, (byte) 0x73, (byte) 0x30, (byte) 0x4e, (byte) 0xec };
// Initialisation
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// Mode
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
String originalText = "hello i am original";
// ///////////////////////////////ENCRYPTING
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphered = cipher.doFinal(originalText.getBytes());
String cipherText = new String(ciphered,"UTF-8");
System.out.println("ciphered: " + cipherText);
// ///////////////////////////////DECRYPTING
cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.**init(Cipher.DECRYPT_MODE**, key, ivSpec);
byte[] plain = **cipher.doFinal(ciphered);**
originalText = new String(plain,"UTF-8");
System.out.println("plaintext: " + originalText);
}
I couldn't figure out what am I doing wrong.any help is deeply appreciated.
also is this proper way to encrypted some data this time I am trying to encrypt 4byte city pin code.
thank in advance
////
I made those changes n' it's working fine but what's the issue if I passes
cipherText.getByte() in cipher.init() function. Like
byte[] plain = cipher.doFinal(cipherText.getByte("UTF-8"));
n' Thanks for all your help.
For decryption you need to initialize the Cipher in DECRYPT_MODE. And also the byte[] to String conversion is not correct (See other answer).
You cannot convert the encrypted bytes to a String like that. "bytes" and "chars" are two entirely different things. remove the code which turns the bytes to a String and back again between encrypting and decrypting and your code should work (as pointed out in other answer, the second step should be using DECRYPT_MODE).
note that you need to be careful when using the platform character encoding to convert between bytes and chars/String, as this may be different on different platforms. this may cause problems if your data needs to move cross platform. it can also be lossy if your default platform encoding doesn't support all the characters in the text you are using.

Categories