import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;

import static javax.crypto.Cipher.ENCRYPT_MODE;

public class TestCipherSpi {

    static class MyCipher extends Cipher {
        protected MyCipher(javax.crypto.CipherSpi cipherSpi, Provider provider, String transformation) {
            super(cipherSpi, provider, transformation);
        }
    }
    
    public static void main(String[] args) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
        Cipher cipher = new MyCipher(mockedCipherSpi, new Provider("a", "b", "c") {
            @Override
            public Provider configure(String configArg) {
                return super.configure(configArg);
            }
        }, "abc");        
        cipher.init(ENCRYPT_MODE, new Key() {
            @Override
            public String getAlgorithm() {
                return null;
            }

            @Override
            public String getFormat() {
                return null;
            }

            @Override
            public byte[] getEncoded() {
                return new byte[0];
            }
        });

        byte[] bytes = cipher.doFinal();
        System.out.println("bytes = " + Arrays.toString(bytes));


    }
    
    static CipherSpi mockedCipherSpi = new CipherSpi() {

        @Override
        protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
            
        }

        @Override
        protected void engineSetPadding(String padding) throws NoSuchPaddingException {

        }

        @Override
        protected int engineGetBlockSize() {
            return 0;
        }

        @Override
        protected int engineGetOutputSize(int inputLen) {
            return 0;
        }

        @Override
        protected byte[] engineGetIV() {
            return new byte[0];
        }

        @Override
        protected AlgorithmParameters engineGetParameters() {
            return null;
        }

        @Override
        protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {

        }

        @Override
        protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {

        }

        @Override
        protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {

        }

        @Override
        protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
            return new byte[0];
        }

        @Override
        protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException {
            return 0;
        }

        @Override
        protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException {
            System.out.println("TestCipherSpi.engineDoFinal called");
            return new byte[] {1,2,3,4,5,42};
        }

        @Override
        protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
            return 0;
        }
    };
    
}
