0

Encryption and Decryption in Java is still very difficult for me to understand. I have been using the following class and methods. I wonder how to improve the safety and how long does the keystring (schlüssel) need to be?

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class AES
{
public static SecretKeySpec makeKey(String schlüssel) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
    byte[] key = (schlüssel).getBytes("UTF-8");
    MessageDigest sha = MessageDigest.getInstance("SHA");
    key = sha.digest(key);
    key = Arrays.copyOf(key, 16);
    return new SecretKeySpec(key, "AES");
}


public static String encryptString(String text, SecretKeySpec schlüssel) throws Exception
{
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, schlüssel);
    byte[] encrypted = cipher.doFinal(text.getBytes());

    BASE64Encoder myEncoder = new BASE64Encoder();
    return myEncoder.encode(encrypted);
}


public static String decryptString(String text, SecretKeySpec schlüssel) throws Exception
{    
    BASE64Decoder myDecoder2 = new BASE64Decoder();
    byte[] crypted2 = myDecoder2.decodeBuffer(text);

    Cipher cipher2 = Cipher.getInstance("AES");
    cipher2.init(Cipher.DECRYPT_MODE, schlüssel);
    byte[] cipherData2 = cipher2.doFinal(crypted2);
    return new String(cipherData2);
}
}

I have been reading about the topic. But I did not understand how to transfer the ideas into my code. Any help is appreciated, please be kind with an encryption beginner. Thank you.

  • Is `schlüssel` some kind of password/passphrase or is it itself randomly generated? How many different possible characters (domain) does it consist of? – Artjom B. Oct 09 '15 at 09:49
  • schlüssel is created either with SecureRandom or by user input. The characters possible are "0123456789abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" –  Oct 09 '15 at 10:20
  • if there is really a `ü` in your variable name (`schlüssel`) -> don't do it. even if its allowed, i wouldn't recommend it (name it `schluessel` instead) – griFlo Oct 09 '15 at 10:36
  • Note that there is a site called [codereview](http://codereview.stackexchange.com) – Maarten Bodewes Oct 09 '15 at 11:03
  • 1
    Another remark; it seems to me that you are misunderstanding a lot about cryptography; I would recommend following at least a base course in crypto before starting to define a protocol, let alone to implement it. – Maarten Bodewes Oct 09 '15 at 11:44

2 Answers2

3

There are a lot of things wrong in this class.

  • the class uses a cryptographic hash instead of a password hash - such as PBKDF2 - to derive a key from the password;
  • you are using ECB mode encryption (the default), you need to use at least CBC, together with an initialization vector (IV);
  • your class doesn't add any integrity protection, in other words the ciphertext is malleable;

It depends on the use case if you require the integrity protection. So I'll point you to this question for more information about password based encryption (PBE). Note that the answers may still deliver malleable ciphertext.

Furthermore the class contains the following Java mistakes:

  • it doesn't distinguish between runtime related exceptions (missing algorithms) and input related exceptions;
  • it uses the default platform encoding for your plaintext;
  • it is using a Sun internal class to perform the Base 64 encoding/decoding.

Note that people will probably point out to you that you are using 128 bit AES encryption. That's however quite strong and - certainly at this point in time - the least of your worries. Upgrading to 192 or 256 bit AES won't increase security significantly.

Community
  • 1
  • 1
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • @ArtjomB. Something like this? Mainly misplaced ')' really, but that was a small mistake giving huge readability issues. – Maarten Bodewes Oct 09 '15 at 11:34
  • I want to share the 32 character String with other people to encrypt/decrypt our data before/after sending it via internet. Therefore I can not use a secret key. Could you post how you would change the code? –  Oct 09 '15 at 13:59
  • If you already have a 32 character string you could just decode it using hexadecimals and directly use it as a 128 bit AESkey using SecretKeySpec. There is a high scoring answer of mine that does it but I'm writing this on a mobile device. Check my answers on my profile. – Maarten Bodewes Oct 09 '15 at 16:36
  • I read your code. Sorry, your code is so complicated I am not able to change it to the form I have got above, meaning one method to create the key, one method to encrypt and one method to decrypt. –  Oct 09 '15 at 18:44
  • Are you saying that you cannot extract the lines that declare & assign the `symKeyData` and `symKey` variables from [this answer](http://stackoverflow.com/questions/4551263/how-can-i-convert-string-to-secretkey/8828196#8828196) and put it in a separate method? – Maarten Bodewes Oct 11 '15 at 15:00
  • I will try. I am using a plain password, that I can write on a piece of paper an give somebody else. I am not sure about this hex thing in your code. But I try. –  Oct 13 '15 at 16:46
  • Sometimes your codes works and sometimes the decrypt fails. What could be the cause? –  Oct 28 '15 at 13:01
0

Refering to Maarten Bodeswes code I try to bring his code into the form I am using.

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class AESplus 
{
public static SecretKeySpec makeKey(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
password = String.format("%040x", new BigInteger(1,password.getBytes(Charset.forName("UTF-8"))));
    password = password.substring(password.length()-32, password.length());
    final byte[] symKeyData = DatatypeConverter.parseHexBinary(password);
    return new SecretKeySpec(symKeyData, "AES");
}


public static String encryptString(String text, SecretKeySpec key) throws NoSuchAlgorithmException, NoSuchPaddingException, 
                    InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
    final byte[] encodedMessage = text.getBytes(Charset.forName("UTF-8"));

    final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    final int blockSize = cipher.getBlockSize();

    // generate random IV using block size
    final byte[] ivData = new byte[blockSize];
    final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
    rnd.nextBytes(ivData);
    final IvParameterSpec iv = new IvParameterSpec(ivData);

    cipher.init(Cipher.ENCRYPT_MODE, key, iv);
    final byte[] encryptedMessage = cipher.doFinal(encodedMessage);

    // concatenate IV and encrypted message
    final byte[] ivAndEncryptedMessage = new byte[ivData.length + encryptedMessage.length];
    System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize);
    System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage, blockSize, encryptedMessage.length);
    return DatatypeConverter.printBase64Binary(ivAndEncryptedMessage);
}


public static String decrytString(String crypttext, SecretKeySpec key) throws NoSuchAlgorithmException, NoSuchPaddingException, 
                        InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException 
{    
    final byte[] ivAndEncryptedMessage = DatatypeConverter.parseBase64Binary(crypttext);

    final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    final int blockSize = cipher.getBlockSize();

    // retrieve random IV from start of the received message
    final byte[] ivData = new byte[blockSize];
    System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize);
    final IvParameterSpec iv = new IvParameterSpec(ivData);

    // retrieve the encrypted message itself
    final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length - blockSize];
    System.arraycopy(ivAndEncryptedMessage, blockSize, encryptedMessage, 0, encryptedMessage.length);

    cipher.init(Cipher.DECRYPT_MODE, key, iv);
    final byte[] encodedMessage = cipher.doFinal(encryptedMessage);

    // concatenate IV and encrypted message
    final String message = new String(encodedMessage,Charset.forName("UTF-8"));

    return message;
}

}

  • Please use the edit link on your question to add additional information. The Post Answer button should be used only for complete answers to the question. – Lol4t0 Oct 20 '15 at 20:01
  • @Lol4t0 The answer is complete ! –  Oct 21 '15 at 05:33
  • I had to throw this solution out again. It was not reliable and I could not figure out why. –  Nov 04 '15 at 09:01