1

In my Android application I want to encrypt one line of text with an ability to decrypt it.

I don't have experience with javax.crypto but I found this answer Encrypt Password in Configuration Files? and implemented something similar.

Code:

public String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
    SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
    Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
    pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, ITERATIONS));
    return Base64.encodeToString(pbeCipher.doFinal(property.getBytes("UTF-8")), Base64.DEFAULT);
}

public String decrypt(String property) throws GeneralSecurityException, IOException {
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
    SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
    Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
    pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, ITERATIONS));
    return new String(pbeCipher.doFinal(Base64.decode(property, Base64.DEFAULT)), "UTF-8");
}

I tested it and it works fine. But there is one thing that bothers me, these methods can throw GeneralSecurityException, UnsupportedEncodingException, and IOException.

Question: could be there a case when a text was successfully encrypted but after some circumstances (new Android version with new VM or something like that) decrypt method will throw GeneralSecurityException or IOException and app won't be able to decrypt that text?

Community
  • 1
  • 1
Vasyl Glodan
  • 556
  • 1
  • 6
  • 22
  • Sure, some/your Android vendor might remove PBEWithMD5AndDES at some point in the future. It can happen considering DES and MD5 should not be used nowadays. Unfortunately, this answer is not really answerable, because I don't think there is a requirement from Google to every vendor to keep PBEWithMD5AndDES. – Artjom B. Apr 16 '17 at 11:57
  • How can anyone say for sure what some vendor will do in the future? – President James K. Polk Apr 16 '17 at 12:00
  • @ArtjomB. aha, I see, and as I understand it's not "safe" to use Cipher at all because every algorithm can be removed in the future? – Vasyl Glodan Apr 16 '17 at 12:03

2 Answers2

3

As for the exceptions: these should not be suddenly thrown on correct input. NoSuchAlgorithmException - a subclass of GeneralSecurityException should be thrown when the algorithm is not supported anymore.

Any API - or in this case cryptographic service - can be withdrawn. But this goes for any function, so in that case your solution space would be empty. In this case at least the algorithm is well defined, so you can always re-import the code from open source or something like that. Of course such an old algorithm could be withdrawn just because it should not be used anymore, but in general Java and Android go for backwards compatibility rather than removal of deprecated functions.


Yes, the algorithm is stable but I guess you need to remove the "b" from the word stable to have a better understanding.

DES is not to be used anymore for any security sensitive material. Only 3-key DES EDE can still be used to offer anything close to security.

The same goes, to a lesser extend, to PBKDF1 which is used in your PBE algorithm.

You need to use another cipher such as AES in GCM mode together with a more modern password hash such as PBKDF2 or even better something like Argon2.

Your SALT constant - I presume it is a constant from the all-uppercase name - should be generated on the fly. Using a constant salt defeats the purpose.


But basically you need to understand cryptography to some level before leveraging it. Othwerwise you need to consult somebody, because copy / paste security doesn't exist.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Got it. I haven't work with cryptography before but wanted to add some basic encryption to filter out majority of "hackers" who can extract `SharedPreferences` from the app but cannot encrypt something more difficult then Base64. – Vasyl Glodan Apr 16 '17 at 12:17
  • Well, DES is marginally better then just base 64 (which doesn't have a key at all), but please try and read PKCS#5 and then find a way of implementing PBKDF2 and AES. Base64 is an *encoding scheme* not an encryption scheme. – Maarten Bodewes Apr 16 '17 at 12:50
0

It can be decrypted easily because it will use DES (CBC) mode of operation. DES only has an effective key size of 56 bits. So the key and the can be brute forced regardless of the (PBKDF1) key derivation.

MD5, while considered broken by itself, is less of an issue when it is used within PBKDF1 - as long as the password contains enough entropy of course.

If possible you should upgrade to Password-Based Encryption (PBE) using PBKDF2 and AES. Beware that PBE usually uses CBC mode encryption, so it is not suitable for transport protocols.

It is a complete task , you just import it and use it...

package com.example.siman.friend_pro;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static javax.crypto.Cipher.getInstance;

public class Encryptor4j
{
    private static byte[] salt = {
            (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
            (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
    };
    private static Cipher ecipher;
    private static Cipher dcipher;
    private static String Property = "youkey";
    private static int iterationCount = 19;

    public static String encrypt(String Text)
    {
        String returnvalue=null;
        try {
            returnvalue = Encryptor4j.form1( Text );
        }
        catch (NoSuchAlgorithmException | InvalidKeySpecException |
                NoSuchPaddingException | InvalidKeyException |
                InvalidAlgorithmParameterException | IllegalBlockSizeException |
                BadPaddingException | IOException e) {
            e.printStackTrace();
        }
        return returnvalue;
    }
    public static String decrypt(String Text)
    {
        String returnvalue=null;
        try {
            returnvalue = Encryptor4j.form2( Text );
        }
        catch (NoSuchAlgorithmException | InvalidKeySpecException |
                NoSuchPaddingException | InvalidKeyException |
                InvalidAlgorithmParameterException | IllegalBlockSizeException |
                BadPaddingException | IOException e) {
            e.printStackTrace();
        }
        return returnvalue;
    }


    private static String form1(String Text)
            throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException
    {
        //Key generation for enc and desc
        KeySpec keySpec = new PBEKeySpec(Property.toCharArray(), salt, iterationCount);
        SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
        // Prepare the parameter to the ciphers
        AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

        //Enc process
        ecipher = getInstance(key.getAlgorithm());
        ecipher.init( ENCRYPT_MODE, key, paramSpec);
        String charSet = "UTF-8";
        byte[] in = Text.getBytes(charSet);
        byte[] out = ecipher.doFinal(in);
        String encStr = new String( android.util.Base64.encode( out,0 ) );
        //String encStr = new String(Base64.getEncoder().encode(out));
        return encStr;
    }

    private static String form2(String Text)
            throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, IOException
    {
        //Key generation for enc and desc
        KeySpec keySpec = new PBEKeySpec(Property.toCharArray(), salt, iterationCount);
        SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
        // Prepare the parameter to the ciphers
        AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
        //Decryption process; same key will be used for decr
        dcipher = getInstance(key.getAlgorithm());
        dcipher.init( DECRYPT_MODE, key, paramSpec);
        //byte[] enc = Base64.getDecoder().decode(encryptedText);
        byte[] enc = android.util.Base64.decode( Text.getBytes(),0 );
        byte[] utf8 = dcipher.doFinal(enc);
        String charSet = "UTF-8";
        String plainStr = new String(utf8, charSet);
        return plainStr;
    }
}
Sohaib Aslam
  • 1,245
  • 17
  • 27