Files Encryption Java

2019-04-17 13:03发布

问题:

I'm making a system that has some data in it, and I want it to take this data (in XML format) and save it as an encrypted string in a txt file, and later on when the software opens again, decrypt the file and read it as normal. I already have all the code to convert the xml to a string, I already have the code to save it, I just need some help with the encryption/decryption code?

Note: I did find some code to encrypt/decrypt, but it seems that I can't split the code into 2 methods.

Here is my attempt:

    public class AesEncrDec
{

public static String encrypt(String Data)
{
    byte[] byteCipherText = null;
    try {
        String plainData=Data,cipherText,decryptedText;
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(128);
        SecureRandom rnd = new SecureRandom();
        SecretKey secretKey = keyGen.generateKey();
        IvParameterSpec iv;
        iv = new IvParameterSpec(rnd.generateSeed(16));
        Cipher aesCipher = Cipher.getInstance("AES");
        aesCipher.init(Cipher.ENCRYPT_MODE,secretKey,iv); 
        byte[] byteDataToEncrypt = plainData.getBytes();
        byteCipherText = aesCipher.doFinal(byteDataToEncrypt);
        cipherText = new BASE64Encoder().encode(byteCipherText);
        return new String(byteCipherText);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidAlgorithmParameterException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    }
    return new String(byteCipherText);
}

public static String dencrypt(String Data)
{
    byte[] byteDecryptedText = null;
    try {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(128);
        IvParameterSpec iv;
        SecureRandom rnd = new SecureRandom();
        iv = new IvParameterSpec(rnd.generateSeed(16));
        Cipher aesCipher = Cipher.getInstance("AES");
        SecretKey secretKey = keyGen.generateKey();
        aesCipher.init(Cipher.ENCRYPT_MODE,secretKey,iv);
        byteDecryptedText = aesCipher.doFinal(Data.getBytes());
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidAlgorithmParameterException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(AesEncrDec.class.getName()).log(Level.SEVERE, null, ex);
    }
    return new String(byteDecryptedText);
}
}

EDIT : In responce to @Libin here are the errors

Mar 24, 2014 6:27:42 PM PrefsReadAndWrite.AesEncrDec decrypt
SEVERE: null
javax.crypto.BadPaddingException: Given final block not properly padded
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
    at javax.crypto.Cipher.doFinal(Cipher.java:2121)
    at PrefsReadAndWrite.AesEncrDec.decrypt(AesEncrDec.java:61)
    at PrefsReadAndWrite.AesEncrDec.decryptedString(AesEncrDec.java:104)
    at smarthouse.SmartHouse.main(SmartHouse.java:12)

Exception in thread "main" java.lang.NullPointerException
    at java.lang.String.<init>(String.java:554)
    at PrefsReadAndWrite.AesEncrDec.decryptedString(AesEncrDec.java:105)
    at smarthouse.SmartHouse.main(SmartHouse.java:12)
Java Result: 1

回答1:

You have to generate the key only once and use it for encrypt and decrypt.. Use this code ...

AppSecurity class should be used to generate new key and Encrypt/Decrypt

public class AppSecurity{
private AppSecurity() {}

public static byte[] encrypt(byte[] key , byte[] data) {
    SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
    try {
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE,keySpec);
        return cipher.doFinal(data);
    }
    catch (NoSuchAlgorithmException e){ }
    catch (NoSuchPaddingException e){ }
    catch (InvalidKeyException e){ }
    catch (BadPaddingException e){ }
    catch (IllegalBlockSizeException e) {}
    return null;
}

public static byte[] decrypt(byte[] key , byte[] encryptedData) {
    SecretKeySpec keySpec = new SecretKeySpec(key ,"AES");
    try {
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        return cipher.doFinal(encryptedData);
    }
    catch (NoSuchAlgorithmException e) {}
    catch (NoSuchPaddingException e) { }
    catch (InvalidKeyException e) { }
    catch (BadPaddingException e) {}
    catch (IllegalBlockSizeException e) {}
    return null;
}

/**
 * method to generate a secure key. call this when app starts
 * @return
 */
public static byte[] generateKey(){
    try{
        // create an AES algorithm instance.
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
        keyGenerator.init(128,secureRandom);
        SecretKey secretKey = keyGenerator.generateKey();
        return secretKey.getEncoded();
    }
    catch (NoSuchAlgorithmException e){
        return null;
    }
  }
}

Method to Encrypt the string.

 private static byte[] encryptedByte(String s) {
    return AppSecurity.encrypt(YourApplication.getSecretKey(),toBytes(s));
 }

Method to Decrypt the byte.

  private static String decryptedString(byte[] blob) {
    // here getSecretKey() should be the one used on encryption
    byte[] decrypted =  AppSecurity.decrypt(YourApplication.getSecretKey(),blob);
    return toString(decrypted);

     }

Method to convert Byte to String

public static String toString(byte[] bytes) {
  try {
    String s = new String(bytes ,"UTF-8");
     return s;
     } catch (UnsupportedEncodingException e) {
      return null;
     }
  }

Method to convert String to Byte

 public static byte[] toBytes(String s) {
   try {
       return s.getBytes("UTF-8");
    } catch (UnsupportedEncodingException e) {return null;}
   }

Detail on how to use this in your application : initialize initSecurity() method on your application/applet class and store the return key into a variable and make use of this on runtime.

Assuming class name as YourApplication.java

 // initialize it on your app startup
 String mSecretKey = initSecurity()

 // call this method when you encrypt /decrypt
 public static byte[] getSecretKey() { return mSecretKey; } 

//Add this method to read from file or generate one new key

 private void initSecurity() {
    final String secretFile = "secure_file";
    boolean keyExists = false;
    //check if secret key exist in secure file
    try {
        FileInputStream inputStream = openFileInput(secretFile);
        mSecretKey = new byte[16];
        int result = inputStream.read(mSecretKey);
        if(result >0) {
            keyExists = true;
        }
        inputStream.close();
    }
    catch (FileNotFoundException e) {}
    catch (IOException e){}
    if(!keyExists) {
        // generate a key
        mSecretKey = AppSecurity.generateKey();
        if(mSecretKey != null) {
            // write in a secure file inside the app
            try {
                // MODE_PRIVATE will create the file (or replace a file of the same name)
                // and make it private to the application.
                FileOutputStream outputStream = openFileOutput(secretFile,Context.MODE_PRIVATE);
                outputStream.write(mSecretKey);
                outputStream.close();
            }
            catch (FileNotFoundException e){}
            catch (IOException e) {}
        }
    }
}


回答2:

You don't define the padding to be used, so an implementation defined padding is used. According to your error, this is probably "NoPadding" in your case. Specify padding and mode of operation, e.g. AES/CBC/PKCS5Padding. Since you now will be using CBC, you also need an initialization vector:

IvParameterSpec iv = new IvParameterSpec(rnd.generateSeed(16));
aesCipher.init(Cipher.ENCRYPT_MODE,secretKey,iv);

where rnd is a SecureRandom instance. Also returning the data as String is a bad idea, use the bytes directly. And of course follow Libins advice to use the same key for encryption and decryption,.