0
static void encrypt() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
    // Here you read the cleartext.
    FileInputStream fis = new FileInputStream("data/cleartext");
    // This stream write the encrypted text. This stream will be wrapped by another stream.
    FileOutputStream fos = new FileOutputStream("data/encrypted");

    // Length is 16 byte
    SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES");
    // Create cipher
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, sks);
    // Wrap the output stream
    CipherOutputStream cos = new CipherOutputStream(fos, cipher);
    // Write bytes
    int b;
    byte[] d = new byte[8];
    while((b = fis.read(d)) != -1) {
        cos.write(d, 0, b);
    }
    // Flush and close streams.
    cos.flush();
    cos.close();
    fis.close();
}

static void decrypt() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
    FileInputStream fis = new FileInputStream("data/encrypted");

    FileOutputStream fos = new FileOutputStream("data/decrypted");
    SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, sks);
    CipherInputStream cis = new CipherInputStream(fis, cipher);
    int b;
    byte[] d = new byte[8];
    while((b = cis.read(d)) != -1) {
        fos.write(d, 0, b);
    }
    fos.flush();
    fos.close();
    cis.close();
}   

I am using these functions for encrypt/ decrypt file, but on some devices I get incorrect data.

for example, my correct data is :

one

two

three

four

five

after decrypt :

one

two

three

൰Ẓ㫩

൰Ẓ㫩

I have used postDelayed() function But it did not matter !

 decrypt();
new Handler().postDelayed(new Runnable() {

    @Override
    public void run() {
    // TODO Auto-generated method stub

            getContentsFile();
            }
        }, 7000);

the file size is 80 Kilobytes!

it had problem on emulator!! it had problem on samsung gt-s7562, but on galaxy s4 everything is ok !!

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
S.M_Emamian
  • 17,005
  • 37
  • 135
  • 254

2 Answers2

0

I see at least two potential platform compatibility issues with your code:

  1. Never call getBytes() without declaring a charset:

    SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES");
    

    should be: (e.g.)

    SecretKeySpec sks = new SecretKeySpec(
        "MyDifficultPassw".getBytes("UTF-8"), "AES");
    
  2. Always specify a complete transformation, e.g. "AES/CBC/PKCS5Padding":

    Cipher cipher = Cipher.getInstance("AES");
    

    should be (e.g.)

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    

This alone could be the cause of your problem. Different platforms have different default character sets, which means you would get a different string of bytes for your key.

Less commonly, different crypto providers have different defaults when you selet "AES". Some will do ECB-mode encryption, some will do CBC-mode encryption. It's always safest to declare exactly what you want.


Side note: you really shouldn't be creating a key from the raw bytes of a string. Use a password derivation method instead, such as PBKDF2.

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
  • Please, do not recommend "US-ASCII" or actually anything else than "UTF-8". – Oleg Estekhin Apr 04 '14 at 13:35
  • @OlegEstekhin Care to explain why? Provided one is consistent with the choice and understands the range of input characters, I'm not sure I see the problem. – Duncan Jones Apr 04 '14 at 13:36
  • 1
    "UTF-8" could be the same consistent choice and it has one less problem because there is no need to care about the range of characters. – Oleg Estekhin Apr 04 '14 at 13:39
  • @OlegEstekhin I often recommend "US-ASCII" in these situations, since people anticipate a one-to-one correspondence between the number of characters in their password and the number of bytes in their key. Anyway, the biggest crime here is deriving a password from raw string bytes and not using a more secure method. – Duncan Jones Apr 04 '14 at 13:41
  • @OlegEstekhin Although perhaps my logic has been flawed in that area (based on some quick testing). I had assumed 16 characters always equated to 16 bytes with US-ASCII, but it appears not to be the case. I'll update my example to UTF-8. – Duncan Jones Apr 04 '14 at 13:43
  • Using the String bytes directly as password is bad. – Robert Apr 04 '14 at 14:54
  • @Robert Yup, hence my last paragraph. – Duncan Jones Apr 04 '14 at 14:57
  • @Duncan LOL. Do you really think that the typical SO user really ready your answer completely? You don't even explain why it is bad. Thank your for lowering the overall quality of Stackoverflow and placing security risks in programs. Thank you very much! – Robert Apr 05 '14 at 10:02
  • @Robert Feel free to suggest an edit to my post, I'll approve if it reads well. Or feel free to add that comment we suggested to you. You seem to think I disagree with your stance, when I don't. I applaud your mission to improve code samples on the Internet. In fact, I'm currently working on a blog post about several common Java security coding errors which I will start to link to in answers where appropriate. – Duncan Jones Apr 05 '14 at 10:08
0

I'm using these two methods for encrypt/decrypt and they work perfectly to me on any device. It's done a slightly different way than you so try comparing both approaches to see what could be working wrong.

For encryption:

Gets: iv vector and the message to encrypt:

public String getEncrypt(final byte[] iv, final String message) throws GeneralSecurityException, NullPointerException {
  if (key.isEmpty())
    throw new NullPointerException();

  final byte[] rawData = key.getBytes(Charset.forName("US-ASCII"));
  if (rawData.length != 16) {
    // If this is not 16 in length, there's a problem with the key size, nothing to do here
    throw new IllegalArgumentException("You've provided an invalid key size");
  }

  final SecretKeySpec seckeySpec = new SecretKeySpec(rawData, "AES");
  final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");

  ciph.init(Cipher.ENCRYPT_MODE, seckeySpec, new IvParameterSpec(iv));

  byte[] encryptedBA = ciph.doFinal(message.getBytes(Charset.forName("US-ASCII")));
  try {
    final String encryptedText = new String(Base64.encode(encryptedBA, Base64.DEFAULT), "UTF-8");
    return encryptedText.toString();
  } 
  catch (final UnsupportedEncodingException e1) { }
  return "";
}

For decryption:

public String getDecrypt(final byte[] encrypted) throws GeneralSecurityException, NullPointerException {
  if (key.isEmpty())
    throw new NullPointerException();

  final byte[] rawData = key.getBytes(Charset.forName("US-ASCII"));
  if (rawData.length != 16) {
    // If this is not 16 in length, there's a problem with the key size, nothing to do here
    throw new IllegalArgumentException("Invalid key size.");
  }

  final SecretKeySpec seckeySpec = new SecretKeySpec(rawData, "AES");

  final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
  ciph.init(Cipher.DECRYPT_MODE, seckeySpec, new IvParameterSpec(new byte[16]));
  final byte[] decryptedmess = ciph.doFinal(encrypted);

  return new String(decryptedmess, Charset.forName("US-ASCII"));
}
nKn
  • 13,691
  • 9
  • 45
  • 62