package org.wikiwebserver.util;

import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;

import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Cryptography {
    
    
    public synchronized static Cipher getCipher(String algorithm, String mode, String padding) 
        throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
        
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        return Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");        
    }
    
    public static Key getKey(String algorithm, byte[] bytes) {
        return new SecretKeySpec(bytes, algorithm);
    }

    public static String encryptString(String clearText, Cipher cipher, Key key, IvParameterSpec ivSpec) throws Exception {
        byte[] plain = clearText.getBytes();
        byte[] encrypted = encrypt(plain, cipher, key, ivSpec);
        BASE64Encoder base64Encoder = new BASE64Encoder();
        return base64Encoder.encode(encrypted);
    }
    
    public static byte[] encrypt(byte[] input, Cipher cipher, Key key, IvParameterSpec ivSpec) throws Exception {
        byte[] buffer = new byte[input.length + key.getEncoded().length];
        synchronized (cipher) {
            cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec );
            int ctLength = cipher.update(input, 0, input.length, buffer, 0);
            ctLength += cipher.doFinal(buffer, ctLength);
            byte[] output = new byte[ctLength];
            System.arraycopy(buffer, 0, output, 0, ctLength);
            return output;
        }
    }
    
    public static String decryptString(String cypherText, Cipher cipher, Key key, IvParameterSpec ivSpec) throws Exception {
        BASE64Decoder base64Decoder = new BASE64Decoder();
        byte[] encrypted = base64Decoder.decodeBuffer(cypherText);      
        byte[] plain = decrypt(encrypted, cipher, key, ivSpec);
        return new String(plain);
    }    

    public static byte[] decrypt(byte[] input, Cipher cipher, Key key, IvParameterSpec ivSpec) throws Exception {
        byte[] buffer = new byte[input.length];
        synchronized (cipher) {
            cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
            int ptLength = cipher.update(input, 0, input.length, buffer, 0);
            ptLength += cipher.doFinal(buffer, ptLength);
            byte[] output = new byte[ptLength];
            System.arraycopy(buffer, 0, output, 0, ptLength);     
            return output;
        }
    }

}
