0

I am able to encrypt an SMS and send it from one simulator (Android 2.2) to another. On the receiving end I am able to do the decryption successfully. But the problem is if do the encryption in one OS version (i.e Android 2.2) and trying to decrypt in another OS version ( Android 2.3 ) i am getting 'Bad padding exception'. I checked that i used the same key on both ends. The code is shown below

public class ED {


    private  String Key;

        public ED() {
            Key = "abc12";   // Assigning default key.
        }

        public ED(String key) {
            // TODO Auto-generated constructor stub
            Key = key;

        }



        public String encrypt(String toEncrypt) throws Exception {
            byte[] rawKey = getRawKey(Key.getBytes("UTF-8"));
            byte[] result = encrypt(rawKey, toEncrypt.getBytes("UTF-8"));
            return toHex(result);
        }



        public  byte[] encrypt(byte[] key, byte[] toEncodeString) throws Exception {

            SecretKeySpec sKeySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);

            byte[] encrypted = cipher.doFinal(toEncodeString);

            return encrypted;
        }

        private  byte[] getRawKey(byte[] key) throws Exception {

            KeyGenerator kGen = KeyGenerator.getInstance("AES");
            SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
            sr.setSeed(key);
            kGen.init(128, sr);
            SecretKey sKey = kGen.generateKey();
            byte[] raw = sKey.getEncoded();
            return raw;

        } 

    /************************************* Decription *********************************************/

            public String decrypt(String encryptedString) throws Exception {

                byte[] rawKey = getRawKey(Key.getBytes("UTF-8"));
                System.out.println("Decrypted Key in bytes : "+rawKey);


                System.out.println("Key in decryption :"+rawKey);


                SecretKeySpec sKeySpec = new SecretKeySpec(rawKey, "AES");
                Cipher cipher = Cipher.getInstance("AES");
                cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
                byte[] decrypted = cipher.doFinal(toByte(encryptedString));
                System.out.println("Decrypted mess in bytes---------->" +decrypted);
                return new String(decrypted);
            }






            public String toHex(byte[] buf) {
                if (buf == null)
                    return "";
                StringBuffer result = new StringBuffer(2*buf.length);
                for (int i = 0; i < buf.length; i++) {
                    appendHex(result, buf[i]);
                }
                return result.toString();
            }
            private final String HEX = "0123456789ABCDEF";
            private void appendHex(StringBuffer sb, byte b) {
                sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
            }

            public byte[] toByte(String hexString) {
                int len = hexString.length()/2;
                byte[] result = new byte[len];
                for (int i = 0; i < len; i++)
                    result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
                return result;
            }

    }

And I am using sendTextMessage() function to send an sms. I read that encryption/decryption doesn't depend on OS but in this case that is not true. Am I missing any important things while configuring the Cipher (in AES) ? Please let me know.

Bo Persson
  • 86,087
  • 31
  • 138
  • 198
rahul dr
  • 15
  • 7
  • Not an answer, just an idea: Have compared the output/input (what you send vs. what you receive) - like, hashed it and compare the result? – PhilW Dec 09 '11 at 13:01
  • If you don't mind performance and Java 1.5 and greater compat I always use sb.append(String.format("%02X", b & 0xFF)). I'm not even sure if that last 0xFF (to make it possitive) is even necessary. – Maarten Bodewes Dec 09 '11 at 13:13
  • Similar problem: [javax.crypto working differently in different versions of Android OS?](http://stackoverflow.com/questions/6316225/javax-crypto-working-differently-in-different-versions-of-android-os/6467548#6467548) – Robert Dec 09 '11 at 13:21
  • Thank you very much Robert. That link solved my problem. The problem comes with SecureRandom key generation. – rahul dr Dec 10 '11 at 06:43

4 Answers4

1

It's setSeed(). It does not do what you think it does: it just adds the entropy of the given seed to the underlying algorithm. You'll probably find out that it returns somehthing different on both platforms. SHA1PRNG is a pseudo random function, but if it is already seeded, it's likely to return different results.

Maarten Bodewes
  • 80,169
  • 13
  • 121
  • 225
  • You are correct Mr.Owlstead. The proble is with SecureRandom() generation, setSeed(). You told "You'll probably find out that it returns somehthing different on both platforms" and that is correct. – rahul dr Dec 10 '11 at 06:54
  • So the best way to solve the problem is use another method to generate a key. For more information c this lik (if any one need the solution) http://stackoverflow.com/questions/6316225/javax-crypto-working-differently-in-different-versions-of-android-os/6467548#6467548 – rahul dr Dec 10 '11 at 06:58
  • Mr. owlstead, I like that :). The key generators listed in the given link should be ok. If you like the answer, don't forget to accept it. – Maarten Bodewes Dec 11 '11 at 05:05
0

If the problem is in the key length, you could derivate a key from your password, instead of using it directly. You could use a Hash (like SHA-1, MD5, etc) and crop it to the correct size (128, 192 or 256 bits), or use PBEKeySpec instead of SecretKeySpec.

That to remove problems with the key length. If the padding problems were in the plaintext, I suggest you to use CipherInputStream and CipherOutputStream, which are more programmer-friendly to use than Cipher.doFinal.

Mister Smith
  • 24,695
  • 17
  • 97
  • 181
0

The problem is with SecureRandom generation. It is giving different results on different platforms. It's because of a bug fix on line 320 (in Gingerbread source) of SHA1PRNG_SecureRandomImpl.java in the engineNextBytes() method where

bits = seedLength << 3 + 64; 

was changed to

bits = (seedLength << 3) + 64; 

Use SecretKeyFactory() to generate a Secure key instead of secure random.

public class Crypto {

Cipher ecipher;
Cipher dcipher;

byte[] salt = { 1, 2, 4, 5, 7, 8, 3, 6 };
int iterationCount = 1979;

Crypto(String passPhase) {
    try {
        // Create the key
        KeySpec keySpec = new PBEKeySpec(passPhase.toCharArray(), salt, iterationCount);
        SecretKey key = SecretKeyFactory.getInstance("PBEWITHSHA256AND128BITAES-CBC-BC").generateSecret(keySpec);
        ecipher = Cipher.getInstance(key.getAlgorithm()); 
        dcipher = Cipher.getInstance(key.getAlgorithm());

        AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

        ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
        dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

    } catch (Exception e) {
        // TODO: handle exception
        //Toast.makeText(this, "I cought ", Toast.LENGTH_LONG).show();
    }


}

public String encrypt(String str) {
    String rVal;
    try {
        byte[] utf8 = str.getBytes("UTF8");

        byte[] enc = ecipher.doFinal(utf8);

        rVal = toHex(enc);

    } catch (Exception e) {
        // TODO: handle exception
        rVal = "Exception Caught "+e.getMessage();
    }
    return rVal;
}


public String decrypt(String str) {
    String rVal;
    try {
        byte[] dec = toByte(str);
        byte[] utf8 = dcipher.doFinal(dec);

        rVal = new String(utf8, "UTF8");

    } catch(Exception e) {
        rVal = "Error in decrypting :"+e.getMessage();
    }
    return rVal;
}

private static byte[] toByte(String hexString ) {
    int len = hexString.length()/2;
    byte[] result = new byte[len];
    for ( int i=0; i<len; i++ ) {
        result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16 ).byteValue();

    }
    return result;
}
private static String toHex(byte[] buf) {
    if (buf == null)
        return "";
    StringBuffer result = new StringBuffer( 2*buf.length);
    for ( int i=0; i<buf.length; i++) {
        appendHex(result, buf[i]);

    }
    return result.toString();
}

private final static String HEX = "0123456789ABCDEF";

private static void appendHex(StringBuffer sb, byte b) {
    sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}


}
Bo Persson
  • 86,087
  • 31
  • 138
  • 198
rahul dr
  • 15
  • 7
0

Don't rely on KeyGenerator to generate the same key just because you seeded the RNG the same way. If you are pre-sharing a key, share the key, not the seed.

You should also specify the encryption transform completely: "AES/ECB/PKCS5Padding"

Finally, ECB mode is not secure for general use.

See another answer of mine for an example to perform encryption correctly with the JCE.

Community
  • 1
  • 1
erickson
  • 249,448
  • 50
  • 371
  • 469