Java AES encryption and decryption

后端 未结 6 1380
独厮守ぢ
独厮守ぢ 2020-12-07 15:48

I would like to encrypt and decrypt a password using 128 bit AES encryption with 16 byte key. I am getting javax.crypto.BadPaddingException error while decrypt

相关标签:
6条回答
  • 2020-12-07 16:26

    You state that you want to encrypt/decrypt a password. I'm not sure exactly of what your specific use case is but, generally, passwords are not stored in a form where they can be decrypted. General practice is to salt the password and use suitably powerful one-way hash (such as PBKDF2).

    Take a look at the following link for more information.

    http://crackstation.net/hashing-security.htm

    0 讨论(0)
  • 2020-12-07 16:34
    import javax.crypto.*;    
    import java.security.*;  
    public class Java {
    
    private static SecretKey key = null;         
       private static Cipher cipher = null; 
    
       public static void main(String[] args) throws Exception
       {
    
          Security.addProvider(new com.sun.crypto.provider.SunJCE());
    
          KeyGenerator keyGenerator =
             KeyGenerator.getInstance("DESede");
          keyGenerator.init(168);
          SecretKey secretKey = keyGenerator.generateKey();
          cipher = Cipher.getInstance("DESede");
    
          String clearText = "I am an Employee";
          byte[] clearTextBytes = clearText.getBytes("UTF8");
    
          cipher.init(Cipher.ENCRYPT_MODE, secretKey);
          byte[] cipherBytes = cipher.doFinal(clearTextBytes);
          String cipherText = new String(cipherBytes, "UTF8");
    
          cipher.init(Cipher.DECRYPT_MODE, secretKey);
          byte[] decryptedBytes = cipher.doFinal(cipherBytes);
          String decryptedText = new String(decryptedBytes, "UTF8");
    
          System.out.println("Before encryption: " + clearText);
          System.out.println("After encryption: " + cipherText);
          System.out.println("After decryption: " + decryptedText);
       }
    }
    
    
    // Output
    
    /*
    Before encryption: I am an Employee  
    After encryption: }?ス?スj6?スm?スZyc?ス?ス*?ス?スl#l?スdV  
    After decryption: I am an Employee  
    */
    
    0 讨论(0)
  • 2020-12-07 16:38

    Try this, a simpler solution.

    byte[] salt = "ThisIsASecretKey".getBytes();
    Key key = new SecretKeySpec(salt, 0, 16, "AES");
    
    Cipher cipher = Cipher.getInstance("AES");
    
    0 讨论(0)
  • 2020-12-07 16:42

    If for a block cipher you're not going to use a Cipher transformation that includes a padding scheme, you need to have the number of bytes in the plaintext be an integral multiple of the block size of the cipher.

    So either pad out your plaintext to a multiple of 16 bytes (which is the AES block size), or specify a padding scheme when you create your Cipher objects. For example, you could use:

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

    Unless you have a good reason not to, use a padding scheme that's already part of the JCE implementation. They've thought out a number of subtleties and corner cases you'll have to realize and deal with on your own otherwise.


    Ok, your second problem is that you are using String to hold the ciphertext.

    In general,

    String s = new String(someBytes);
    byte[] retrievedBytes = s.getBytes();
    

    will not have someBytes and retrievedBytes being identical.

    If you want/have to hold the ciphertext in a String, base64-encode the ciphertext bytes first and construct the String from the base64-encoded bytes. Then when you decrypt you'll getBytes() to get the base64-encoded bytes out of the String, then base64-decode them to get the real ciphertext, then decrypt that.

    The reason for this problem is that most (all?) character encodings are not capable of mapping arbitrary bytes to valid characters. So when you create your String from the ciphertext, the String constructor (which applies a character encoding to turn the bytes into characters) essentially has to throw away some of the bytes because it can make no sense of them. Thus, when you get bytes out of the string, they are not the same bytes you put into the string.

    In Java (and in modern programming in general), you cannot assume that one character = one byte, unless you know absolutely you're dealing with ASCII. This is why you need to use base64 (or something like it) if you want to build strings from arbitrary bytes.

    0 讨论(0)
  • 2020-12-07 16:48

    Here is the implementation that was mentioned above:

    import javax.crypto.Cipher;
    import javax.crypto.SecretKey;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.PBEKeySpec;
    import javax.crypto.spec.SecretKeySpec;
    
    import org.apache.commons.codec.binary.Base64;
    import org.apache.commons.codec.binary.StringUtils;
    
    try
    {
        String passEncrypt = "my password";
        byte[] saltEncrypt = "choose a better salt".getBytes();
        int iterationsEncrypt = 10000;
        SecretKeyFactory factoryKeyEncrypt = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
        SecretKey tmp = factoryKeyEncrypt.generateSecret(new PBEKeySpec(
                passEncrypt.toCharArray(), saltEncrypt, iterationsEncrypt,
                128));
        SecretKeySpec encryptKey = new SecretKeySpec(tmp.getEncoded(),
                "AES");
    
        Cipher aesCipherEncrypt = Cipher
                .getInstance("AES/ECB/PKCS5Padding");
        aesCipherEncrypt.init(Cipher.ENCRYPT_MODE, encryptKey);
    
        // get the bytes
        byte[] bytes = StringUtils.getBytesUtf8(toEncodeEncryptString);
    
        // encrypt the bytes
        byte[] encryptBytes = aesCipherEncrypt.doFinal(bytes);
    
        // encode 64 the encrypted bytes
        String encoded = Base64.encodeBase64URLSafeString(encryptBytes);
    
        System.out.println("e: " + encoded);
    
        // assume some transport happens here
    
        // create a new string, to make sure we are not pointing to the same
        // string as the one above
        String encodedEncrypted = new String(encoded);
    
        //we recreate the same salt/encrypt as if its a separate system
        String passDecrypt = "my password";
        byte[] saltDecrypt = "choose a better salt".getBytes();
        int iterationsDecrypt = 10000;
        SecretKeyFactory factoryKeyDecrypt = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
        SecretKey tmp2 = factoryKeyDecrypt.generateSecret(new PBEKeySpec(passDecrypt
                .toCharArray(), saltDecrypt, iterationsDecrypt, 128));
        SecretKeySpec decryptKey = new SecretKeySpec(tmp2.getEncoded(), "AES");
    
        Cipher aesCipherDecrypt = Cipher.getInstance("AES/ECB/PKCS5Padding");
                aesCipherDecrypt.init(Cipher.DECRYPT_MODE, decryptKey);
    
        //basically we reverse the process we did earlier
    
        // get the bytes from encodedEncrypted string
        byte[] e64bytes = StringUtils.getBytesUtf8(encodedEncrypted);
    
        // decode 64, now the bytes should be encrypted
        byte[] eBytes = Base64.decodeBase64(e64bytes);
    
        // decrypt the bytes
        byte[] cipherDecode = aesCipherDecrypt.doFinal(eBytes);
    
        // to string
        String decoded = StringUtils.newStringUtf8(cipherDecode);
    
        System.out.println("d: " + decoded);
    
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    
    0 讨论(0)
  • 2020-12-07 16:49

    Complete example of encrypting/Decrypting a huge video without throwing Java OutOfMemoryException and using Java SecureRandom for Initialization Vector generation. Also depicted storing key bytes to database and then reconstructing same key from those bytes.

    https://stackoverflow.com/a/18892960/185022

    0 讨论(0)
提交回复
热议问题