3

In my app we are going to send encrypted data to the web service. I am using the following code to encrypt and decrypt the data -

public class DecEn {
    private static final String password = "test";
    private static String salt;
    private static int pswdIterations = 65536;
    private static int keySize = 256;
    private byte[] ivBytes;

    public String encrypt(String plainText) throws Exception {

        //get salt
        salt = generateSalt();
        byte[] saltBytes = salt.getBytes("UTF-8");

        // Derive the key
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(
                password.toCharArray(),
                saltBytes,
                pswdIterations,
                keySize
        );

        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

        //encrypt the message
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        AlgorithmParameters params = cipher.getParameters();
        ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
        byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
        return new Base64().encodeAsString(encryptedTextBytes);
//      return Base64.encodeBase64String(encryptedTextBytes);
//      return Hex.encodeHexString(encryptedTextBytes);
    }

    @SuppressWarnings("static-access")
    public String decrypt(String encryptedText) throws Exception {

        byte[] saltBytes = salt.getBytes("UTF-8");
        byte[] encryptedTextBytes = new Base64().decodeBase64(encryptedText);
//      byte[] encryptedTextBytes = Base64.decodeBase64(encryptedText);
//      byte[] encryptedTextBytes = Hex.decodeHex(encryptedText.toCharArray());

        // Derive the key
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(
                password.toCharArray(),
                saltBytes,
                pswdIterations,
                keySize
        );

        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

        // Decrypt the message
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));


        byte[] decryptedTextBytes = null;
        try {
            decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return new String(decryptedTextBytes);
    }

    public String generateSalt() {
        SecureRandom random = new SecureRandom();
        byte bytes[] = new byte[20];
        random.nextBytes(bytes);
        String s = new String(bytes);
        return s;
    }
}

The above code stores IV and salt after they are generated and reuses them to decrypt the data. This will not be true in case of collaboration between app and web service.

What I am actually confused about is that IV and salt are being generated anew each time data is encrypted which is the recommended procedure as far as I could tell from my Googling but as this data is being sent to a web service the IV and the salt has to be available there which means I will have to send it along with my data. Doesn't this defeat the purpose of encryption? As it makes the information available to the attacker.

Or the other way seems to be to use static IV and salt which again makes the encryption susceptible to attacks. That encryption is being used successfully world wide indicates that my understanding of the subject is lacking somewhere.

Please help me by clearing up my doubts.

  • 1
    That doesn't "defeat the purpose of encryption". ​ ​ – user49075 Jul 7 '15 at 6:14
  • 3
    Why don't you simply use TLS? – CodesInChaos Jul 7 '15 at 8:06
3

Initialization vectors aren't meant to be private. The goal of an IV isn't to strengthen the key but to make sure that no two identical (or similar) clear text will result in the same (or similar) cipher text.

It works like this: AES (along with most - but not all - modern crypto algorithm) is a block cipher. It only operate on a fixed amount of data (in AES case, 128 bits). So, in order for identical data blocks within the source data (i.e. using the same key) to be identical, it is usual to specify a mode of operation that introduce some form of change between two data blocks.

In your case, you've selected CBC which basically XOR the clear text of the block N with the cipher text of block N-1 before encrypting it.

This works well except for the first block: there is no previous block to mix it with and therefore, if you don't do something about it, if the clear text starts with the same 128 bits will be similar if the same key is used for encryption. This is a real problem because it is quite frequent for two messages to have the same first 128 bits. Furthermore, depending on the application, it is possible to deduce the meaning of the message without even decrypting it (think of "yes" and "no" answers, for instances).

That what the IV is for: it makes sure that, as long as you don't reuse the IV, you won't have to worry about two messages yielding identical clear text.

Another point worth making is that you could do the same with changing the key. There is two problems here:

  • Key are often picked up on the fly and therefore only statistically unique (which isn't a problem).
  • Key are expensive to change: at best, you need to re-negociate a key (for instance, with DH). At worth, you need to re-create a new key and exchange it through a secure channel.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.