// Path: src/main/java/net/seleucus/wsp/crypto/WebSpaUtils.java
// public class WebSpaUtils {
// 
// 	private static final int ITERATIONS = 1024;
// 	
// 	protected static byte[] digest(byte[] value) {
// 		for (int i = 0; i < ITERATIONS; i++) {
// 			value = DigestUtils.sha512(value);
//         }
//         
// 		return value;
//     }
// 	
// 	protected static byte[] xor(final byte[] inputByteArray, final byte timeByte) {
// 		final byte[] outputByteArray = new byte[inputByteArray.length];
// 
// 		for (int intCount = 0; intCount < inputByteArray.length; intCount++) {
// 			outputByteArray[intCount] = (byte) (inputByteArray[intCount] ^ timeByte);
// 		}
// 		
// 		return outputByteArray;
// 	}
// 	
// 	protected static boolean equals(byte[] first, byte[] second) {
//         if(first == null && second == null){
//             return false;
//         }
// 		return Arrays.equals(first, second);
// 	}
// }
// 
// Path: src/main/java/net/seleucus/wsp/crypto/fwknop/fields/DigestType.java
// public enum DigestType {
// 
//     MD5(1, "MD5"),
//     SHA1(2, "SHA-1"),
//     SHA256(3, "SHA-256"),
//     SHA384(4, "SHA-384"),
//     SHA512(5, "SHA-512");
// 
//     private final int id;
//     private final String algorithmName;
// 
//     private DigestType(final int id, final String algorithmName) {
//         this.id = id;
//         this.algorithmName = algorithmName;
//     }
// 
//     public int getId() {
//         return id;
//     }
// 
//     public String algorithmName() {
//         return algorithmName;
//     }
// }
// 
// Path: src/main/java/net/seleucus/wsp/crypto/fwknop/fields/EncryptionMode.java
// public enum EncryptionMode {
// 
//     ECB(1, "ECB"),
//     CBC(2, "CBC"),
//     CFB(3, "CFB"),
//     PCBC(4, "PCBC"),
//     OFB(5, "OFB"),
//     CTR(6, "CTR");
// 
//     private final int id;
//     private final String name;
// 
//     private EncryptionMode(final int id, final String name) {
//         this.id = id;
//         this.name = name;
//     }
// 
//     public int id() {
//         return id;
//     }
// 
//     public String modeName() {
//         return name;
//     }
// }
// 
// Path: src/main/java/net/seleucus/wsp/crypto/fwknop/fields/EncryptionType.java
// public enum EncryptionType {
// 
//     AES(1, "AES", 16);
// 
//     private final int id;
//     private final String name;
//     private final int blockSize;
// 
//     private EncryptionType(final int id, final String name, int blockSize) {
//         this.id = id;
//         this.name = name;
//         this.blockSize = blockSize;
//     }
// 
//     public int getId() {
//         return id;
//     }
// 
//     public String algorithmName() {
//         return name;
//     }
// 
//     public int getBlockSize() {
//         return blockSize;
//     }
// }
// 
// Path: src/main/java/net/seleucus/wsp/crypto/fwknop/fields/HmacType.java
// public enum HmacType {
// 
//     HmacMD5(0, "HmacMD5", 16, 22),
//     HmacSHA1(1, "HmacSHA1", 20, 27),
//     HmacSHA256(2, "HmacSHA256", 32, 43),
//     HmacSHA384(3, "HmacSHA384", 48, 64),
//     HmacSHA512(4, "HmacSHA512", 64, 86);
// 
//     private final int id;
//     private final String algorithmName;
//     private final int length;
//     private final int base64Length;
// 
//     private HmacType(int id, String algorithmName, int length, int base64Length) {
//         this.id = id;
//         this.algorithmName = algorithmName;
//         this.length = length;
//         this.base64Length = base64Length;
//     }
// 
//     public int id() {
//         return id;
//     }
// 
//     public String algorithmName() {
//         return algorithmName;
//     }
// 
//     public int length() {
//         return length;
//     }
// 
//     public int base64Length(){
//         return base64Length;
//     }
// }


import net.seleucus.wsp.crypto.WebSpaUtils;
import net.seleucus.wsp.crypto.fwknop.fields.DigestType;
import net.seleucus.wsp.crypto.fwknop.fields.EncryptionMode;
import net.seleucus.wsp.crypto.fwknop.fields.EncryptionType;
import net.seleucus.wsp.crypto.fwknop.fields.HmacType;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static org.apache.commons.codec.Charsets.UTF_8;
package net.seleucus.wsp.crypto.fwknop;




/**
 *
 * @author pgolen
 */
public final class FwknopSymmetricCryptoService extends WebSpaUtils {

    private static final String PADDING_STRATEGY = "PKCS5Padding";

    private final static String FWKNOP_ENCRYPTION_HEADER = "U2FsdGVkX1";

    private final static byte SALT_LEN = 8;
    private final static byte IV_LEN = 16;
    private final static byte KEY_LEN = 32;

    private final SecureRandom secureRandom;
    private final EncryptionType encryptionType;
    private final EncryptionMode encryptionMode;
    private final DigestType digestType;

// Path: src/main/java/net/seleucus/wsp/crypto/WebSpaUtils.java
// public class WebSpaUtils {
// 
// 	private static final int ITERATIONS = 1024;
// 	
// 	protected static byte[] digest(byte[] value) {
// 		for (int i = 0; i < ITERATIONS; i++) {
// 			value = DigestUtils.sha512(value);
//         }
//         
// 		return value;
//     }
// 	
// 	protected static byte[] xor(final byte[] inputByteArray, final byte timeByte) {
// 		final byte[] outputByteArray = new byte[inputByteArray.length];
// 
// 		for (int intCount = 0; intCount < inputByteArray.length; intCount++) {
// 			outputByteArray[intCount] = (byte) (inputByteArray[intCount] ^ timeByte);
// 		}
// 		
// 		return outputByteArray;
// 	}
// 	
// 	protected static boolean equals(byte[] first, byte[] second) {
//         if(first == null && second == null){
//             return false;
//         }
// 		return Arrays.equals(first, second);
// 	}
// }
// 
// Path: src/main/java/net/seleucus/wsp/crypto/fwknop/fields/DigestType.java
// public enum DigestType {
// 
//     MD5(1, "MD5"),
//     SHA1(2, "SHA-1"),
//     SHA256(3, "SHA-256"),
//     SHA384(4, "SHA-384"),
//     SHA512(5, "SHA-512");
// 
//     private final int id;
//     private final String algorithmName;
// 
//     private DigestType(final int id, final String algorithmName) {
//         this.id = id;
//         this.algorithmName = algorithmName;
//     }
// 
//     public int getId() {
//         return id;
//     }
// 
//     public String algorithmName() {
//         return algorithmName;
//     }
// }
// 
// Path: src/main/java/net/seleucus/wsp/crypto/fwknop/fields/EncryptionMode.java
// public enum EncryptionMode {
// 
//     ECB(1, "ECB"),
//     CBC(2, "CBC"),
//     CFB(3, "CFB"),
//     PCBC(4, "PCBC"),
//     OFB(5, "OFB"),
//     CTR(6, "CTR");
// 
//     private final int id;
//     private final String name;
// 
//     private EncryptionMode(final int id, final String name) {
//         this.id = id;
//         this.name = name;
//     }
// 
//     public int id() {
//         return id;
//     }
// 
//     public String modeName() {
//         return name;
//     }
// }
// 
// Path: src/main/java/net/seleucus/wsp/crypto/fwknop/fields/EncryptionType.java
// public enum EncryptionType {
// 
//     AES(1, "AES", 16);
// 
//     private final int id;
//     private final String name;
//     private final int blockSize;
// 
//     private EncryptionType(final int id, final String name, int blockSize) {
//         this.id = id;
//         this.name = name;
//         this.blockSize = blockSize;
//     }
// 
//     public int getId() {
//         return id;
//     }
// 
//     public String algorithmName() {
//         return name;
//     }
// 
//     public int getBlockSize() {
//         return blockSize;
//     }
// }
// 
// Path: src/main/java/net/seleucus/wsp/crypto/fwknop/fields/HmacType.java
// public enum HmacType {
// 
//     HmacMD5(0, "HmacMD5", 16, 22),
//     HmacSHA1(1, "HmacSHA1", 20, 27),
//     HmacSHA256(2, "HmacSHA256", 32, 43),
//     HmacSHA384(3, "HmacSHA384", 48, 64),
//     HmacSHA512(4, "HmacSHA512", 64, 86);
// 
//     private final int id;
//     private final String algorithmName;
//     private final int length;
//     private final int base64Length;
// 
//     private HmacType(int id, String algorithmName, int length, int base64Length) {
//         this.id = id;
//         this.algorithmName = algorithmName;
//         this.length = length;
//         this.base64Length = base64Length;
//     }
// 
//     public int id() {
//         return id;
//     }
// 
//     public String algorithmName() {
//         return algorithmName;
//     }
// 
//     public int length() {
//         return length;
//     }
// 
//     public int base64Length(){
//         return base64Length;
//     }
// }


// Path: src/main/java/net/seleucus/wsp/crypto/fwknop/FwknopSymmetricCryptoService.java
import net.seleucus.wsp.crypto.WebSpaUtils;
import net.seleucus.wsp.crypto.fwknop.fields.DigestType;
import net.seleucus.wsp.crypto.fwknop.fields.EncryptionMode;
import net.seleucus.wsp.crypto.fwknop.fields.EncryptionType;
import net.seleucus.wsp.crypto.fwknop.fields.HmacType;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static org.apache.commons.codec.Charsets.UTF_8;

package net.seleucus.wsp.crypto.fwknop;




/**
 *
 * @author pgolen
 */
public final class FwknopSymmetricCryptoService extends WebSpaUtils {

    private static final String PADDING_STRATEGY = "PKCS5Padding";

    private final static String FWKNOP_ENCRYPTION_HEADER = "U2FsdGVkX1";

    private final static byte SALT_LEN = 8;
    private final static byte IV_LEN = 16;
    private final static byte KEY_LEN = 32;

    private final SecureRandom secureRandom;
    private final EncryptionType encryptionType;
    private final EncryptionMode encryptionMode;
    private final DigestType digestType;

    public FwknopSymmetricCryptoService(SecureRandom secureRandom, EncryptionType encryptionType, EncryptionMode encryptionMode, DigestType digestType) {