I use some basic cryptography in a project of mine. Because I want my application to run cross-platform and because it seems pretty sophisticated I decided to go with the Java Cryptography Architecture.

I'm pretty fresh with crypto in general, so I'd like to hear someone's opinion who has some experience with that.

First, I have some convenience wrapper classes for the different symmetric ciphers I support:

 public abstract class PwsCipher {
 //=== methods ================================================================
 /**
 * Gets the cipher's block length.
 * @return Block length in bytes.
 */
 int getBlockLength()
 throws NoSuchAlgorithmException, NoSuchPaddingException,
 NoSuchAlgorithmException, NoSuchProviderException {
 return createCipher().getBlockSize();
 }
 
 /**
 * Creates a new {@link javax.crypto.cipher} instance of this
 * cipher.
 * @return Instance of {@link Cipher}.
 */
 Cipher createCipher()
 throws NoSuchAlgorithmException, NoSuchPaddingException,
 NoSuchProviderException {
 String provider = "SC"; // Spongy Castle for android
 if ( !System.getProperty("java.vm.name").equals("Dalvik") ) {
 provider = "BC"; // Bouncy Castle for everything else
 }
 return Cipher.getInstance(getName() + "/CBC/PKCS7Padding", provider);
 }
 //=== getter & setter ========================================================
 
 /**
 * Gets the name of the cipher.
 * @return Cipher name.
 */
 abstract String getName();
 
 /**
 * Gets the key length of the cipher in bytes.
 * @return Key length in bytes.
 */
 abstract int getKeyLength();
 
 }
 
 public class PwsCipherAES extends PwsCipher {
 //=== getter & setter ========================================================
 @Override
 public String getName() {
 return "AES";
 }
 @Override
 public int getKeyLength() {
 return 32;
 }
 }
 
 public class PwsCipherBlowfish extends PwsCipher {
 //=== getter & setter ========================================================
 @Override
 public String getName() {
 return "Blowfish";
 }
 @Override
 public int getKeyLength() {
 return 56;
 }
 }
 
 public class PwsCipherDES3 extends PwsCipher {
 //=== getter & setter ========================================================
 @Override
 public String getName() {
 return "DES3";
 }
 @Override
 public int getKeyLength() {
 return 24;
 }
 }

Then, I have a `Passphrase` class which I use to create a SHA-512 hash and salt for a clear text passphrase:

 public class Passphrase {
 //=== attributes =============================================================
 /** The salt value of the passphrase. */
 private byte[] salt;
 
 /** The hashed passphrase. */
 private byte[] hashed;
 
 //=== constructors ===========================================================
 /**
 * Creates a new instance of {@code Passphrase}.
 * Assumes that the passphprase is not hashed, and that a new, random salt
 * value should be created.
 * @param phrase Clear text passphrase. Must not be hashed yet.
 */
 public Passphrase(String clearPhrase) throws NoSuchAlgorithmException {
 this(clearPhrase, false, null);
 }
 
 /**
 * Creates a new instance of {@code Passphrase}.
 * Assumes that the passphrase is not hashed.
 * @param phrase Clear text passphrase.
 * @param salt Salt of the passphrase. May be {@code null}. If so, a new
 * random salt will be created.
 */
 public Passphrase(String clearPhrase, byte[] salt)
 throws NoSuchAlgorithmException {
 this(clearPhrase, false, salt);
 }
 
 /**
 * Creates a new instance of {@code Passphrase}.
 * @param phrase Clear text or hashed passphrase.
 * @param isHashed Tells if the passphrase is already hashed. If {@code false}
 * the hash of the clear text passphrase will be created.
 * @param salt Salt of the passphrase. May be {@code null}. If so, a new
 * random salt will be created.
 */
 public Passphrase(String phrase, boolean isHashed, byte[] salt)
 throws NoSuchAlgorithmException {
 setSalt(salt);
 setHash(phrase, isHashed);
 }
 //=== static methods =========================================================
 //=== methods ================================================================
 
 /**
 * Creates a random byte array with length 64.
 * @return Random byte array.
 */
 public final byte[] createRandomSalt() {
 Random r = new SecureRandom();
 byte[] slt = new byte[64];
 r.nextBytes(slt);
 return slt;
 }
 
 //=== getter & setter ========================================================
 
 /**
 * Sets the salt of the Passphrase. <br/>
 * May be {@code null}. If that is the case, a new, random salt will created.
 * @param salt Salt to be set. May be {@code null}. In that case a random salt
 * will be generated. Should not be longer than hash length, which is 64 bytes
 */
 private void setSalt(byte[] salt) {
 if ( salt == null ) {
 this.salt = createRandomSalt();
 } else {
 this.salt = salt;
 }
 }
 
 /**
 * Sets the hashed passphrase. <br/>
 * If the passphrase is not hashed, it will be using SHA-512 and the
 * previously set salt.
 * @param phrase Hashed or clear text passphrase.
 * @param isHashed Tells if the passphrase is already hashed.
 */
 private void setHash(String phrase, boolean isHashed)
 throws NoSuchAlgorithmException {
 if ( isHashed ) {
 this.hashed = phrase.getBytes();
 } else {
 MessageDigest md = MessageDigest.getInstance("SHA-512");
 byte[] salted = new byte[phrase.length() + salt.length];
 byte[] clear = phrase.getBytes();
 System.arraycopy(clear, 0, salted, 0, clear.length);
 System.arraycopy(salt, 0, salted, clear.length, salt.length);
 this.hashed = md.digest(salted);
 //@TODO: nullify byte[] clear, as it contains clear text passphrase, which
 // we don't want to be swapped or debugged
 }
 }
 
 /**
 * Gets the hashed passphrase.
 * @return Hashed passphrase.
 */
 public byte[] getHashedPassphrase() {
 return hashed;
 }
 
 /**
 * Gets the salt of the passphrase.
 * @return Salt.
 */
 public byte[] getSalt() {
 return salt;
 }
 }

Finally there is my crypto code:

 /**
 * Encrypts the given content with the given passphrase and configured cipher.
 * @param content Content to be encrypted.
 * @return Encrypted byte array.
 */
 private byte[] encrypt(byte[] content)
 throws NoSuchAlgorithmException, NoSuchPaddingException,
 InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
 NoSuchProviderException, InvalidAlgorithmParameterException {
 return performCrypto(content, Cipher.ENCRYPT_MODE);
 }
 
 /**
 * Decrypts the given content with the given passphrase and configured cipher.
 * @param content Content to be decrypted.
 * @return Decrypted byte array.
 */
 private byte[] decrypt(byte[] content)
 throws NoSuchAlgorithmException, NoSuchPaddingException,
 InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
 NoSuchProviderException, InvalidAlgorithmParameterException {
 return performCrypto(content, Cipher.DECRYPT_MODE);
 }
 
 /**
 * Performs the actual crypto operation based on the given mode. <br/>
 * When encrypting, a new random initialization vector will be created, used
 * during encryption and finally prepended to the encrypted data chunk, so
 * it can be retrieved again during decryption.
 * @param content Encrypted or decrypted content.
 * @param mode {@link Cipher.ENCRYPT_MODE} or {@link Cipher.DECRYPT_MODE}.
 * @return Transformed content
 */
 private byte[] performCrypto(byte[] content, int mode)
 throws NoSuchAlgorithmException, NoSuchPaddingException,
 InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
 NoSuchProviderException, InvalidAlgorithmParameterException {
 Cipher c = cipher.createCipher();
 SecretKeySpec key = new SecretKeySpec(Arrays.copyOf(
 passphrase.getHashedPassphrase(), cipher.getKeyLength()),
 cipher.getName());
 byte[] iv = createIV(); // always create iv to be able to get iv 
 // length in decrypt mode
 int offset = 0;
 int len = content.length;
 if ( mode == Cipher.DECRYPT_MODE ) {
 // read prepended iv from encrypted data
 iv = Arrays.copyOf(content, iv.length);
 // offset by length of iv
 offset = iv.length;
 len -= iv.length;
 }
 c.init(mode, key, new IvParameterSpec(iv));
 byte[] cryptoresult = c.doFinal(content, offset, len);
 byte[] finishedarray;
 if ( mode == Cipher.ENCRYPT_MODE ) {
 // prepend iv
 finishedarray = new byte[iv.length + cryptoresult.length];
 System.arraycopy(iv, 0,
 finishedarray, 0,
 iv.length);
 System.arraycopy(cryptoresult, 0,
 finishedarray, iv.length,
 cryptoresult.length);
 } else {
 finishedarray = cryptoresult;
 }
 return finishedarray;
 }
 
 /**
 * Creates a {@link SecureRandom} 16 byte sequence which can be used as
 * initialization vector.
 * @return Random byte sequence.
 */
 private byte[] createIV() {
 Random r = new SecureRandom();
 byte[] iv = new byte[16];
 r.nextBytes(iv);
 return iv;
 }

The clear text data that is supposed to be encrypted will be JSON structures. I'm mostly interested in finding more or less obvious "messups" I have in that code from a security point of view.

If you need access to the complete code, you can access it [here][1].

[1]: https://github.com/Hydrael/pws/tree/java/pws/de/simperium/pws/backend