@Override
  public String decrypt(ZWEncryptedData encryptedData) throws ZWKeyCrypterException {
    try {
      String encrypted = encryptedData.getEncryptedData();
      Cipher decryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM);
      int ivLength = decryptionCipher.getBlockSize();
      String ivHex = encrypted.substring(0, ivLength * 2);
      String encryptedHex = encrypted.substring(ivLength * 2);

      IvParameterSpec ivspec = new IvParameterSpec(ZiftrUtils.hexStringToBytes(ivHex));
      decryptionCipher.init(Cipher.DECRYPT_MODE, this.secretKey, ivspec);
      byte[] decryptedText = decryptionCipher.doFinal(ZiftrUtils.hexStringToBytes(encryptedHex));
      String decrypted = new String(decryptedText, "UTF-8");
      return decrypted;
    } catch (Exception e) {
      throw new ZWKeyCrypterException("Unable to decrypt", e);
    }
  }
  private static byte[] generateIv(int ivLength) {
    SecureRandom random = ZiftrUtils.createTrulySecureRandom();

    if (random == null) {
      String rngError = ZWApplication.getApplication().getString(R.string.zw_dialog_error_rng);
      ZiftrDialogManager.showSimpleAlert(rngError);
    }

    byte[] iv = new byte[ivLength];
    random.nextBytes(iv);
    return iv;
  }
  public static String generateSalt() throws ZWKeyCrypterException {
    try {
      SecureRandom random = ZiftrUtils.createTrulySecureRandom();
      if (random == null) {
        String rngError = ZWApplication.getApplication().getString(R.string.zw_dialog_error_rng);
        ZiftrDialogManager.showSimpleAlert(rngError);
      }

      byte[] salt = new byte[SALT_LENGTH];
      random.nextBytes(salt);
      String saltHex = ZiftrUtils.bytesToHexString(salt);
      try {
        ZLog.log("salt: " + salt);
      } catch (Exception e) {
        System.out.println("salt: " + saltHex);
      }
      return saltHex;
    } catch (Exception e) {
      throw new ZWKeyCrypterException("Unable to generate salt", e);
    }
  }
  @Override
  public ZWEncryptedData encrypt(String clearText) throws ZWKeyCrypterException {
    try {
      Cipher encryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM);

      byte[] iv = generateIv(encryptionCipher.getBlockSize());
      String ivHex = ZiftrUtils.bytesToHexString(iv);
      IvParameterSpec ivSpec = new IvParameterSpec(iv);

      encryptionCipher.init(Cipher.ENCRYPT_MODE, this.secretKey, ivSpec);
      byte[] encryptedText = encryptionCipher.doFinal(clearText.getBytes("UTF-8"));
      String encryptedHex = ZiftrUtils.bytesToHexString(encryptedText);

      // Why are we appending these values?
      // AES requires a random initialization vector (IV). We save the IV
      // with the encrypted value so we can get it back later in decrypt()
      return new ZWEncryptedData(ivHex + encryptedHex);
    } catch (Exception e) {
      System.out.println("error message: " + e.getMessage());
      throw new ZWKeyCrypterException("Unable to encrypt", e);
    }
  }
 public static SecretKey generateSecretKey(String password, String salt)
     throws ZWKeyCrypterException {
   try {
     PBEKeySpec pbeKeySpec =
         new PBEKeySpec(
             password.toCharArray(),
             ZiftrUtils.hexStringToBytes(salt),
             PBE_ITERATION_COUNT,
             KEY_LENGTH);
     SecretKeyFactory factory = SecretKeyFactory.getInstance(PBE_ALGORITHM);
     SecretKey tmp = factory.generateSecret(pbeKeySpec);
     SecretKey secret = new SecretKeySpec(tmp.getEncoded(), SECRET_KEY_ALGORITHM);
     return secret;
   } catch (Exception e) {
     throw new ZWKeyCrypterException("Unable to get secret key", e);
   }
 }
 @Override
 public byte[] decryptToBytes(ZWEncryptedData encryptedData) throws ZWKeyCrypterException {
   return ZiftrUtils.hexStringToBytes(decrypt(encryptedData));
 }