@Override
    public byte[] encrypt(byte[] plainInput, byte[] publicKey) {

        BigInteger n = new BigInteger(publicKey);
        int keySize = publicKey.length;
        int dataBlockSize = keySize + 2 * (keySize / 128);
        int plainBlockSize = keySize - 2;
        int wholeLen = plainInput.length;
        int cryptBlocksLen = (wholeLen / plainBlockSize) * dataBlockSize;
        int dataBlocksLen = cryptBlocksLen + 3 * dataBlockSize;
        int plainBlocksLen = (wholeLen / plainBlockSize) * plainBlockSize;
        byte[] cryptOutput = new byte[dataBlocksLen];

        if (wholeLen > keySize) {
            int percentProgress;
            int prevPercent = -1;
            int plainPlusOneBlockSize = plainBlockSize + 1;
            int rest = wholeLen - plainBlocksLen;
            int halfOfVektor;
            byte[] dataBlock = new byte[dataBlockSize];
            byte[] cryptBlock;
            byte[] plainBlock = new byte[plainBlockSize];
            byte[] plusOnePlainBlock = new byte[plainPlusOneBlockSize];
            byte[] initVektorBlock;
            byte[] firstVektorBlock;
            byte[] secVektorBlock;
            byte[] cryptBlockSize;
            byte[] one = BigInteger.ONE.toByteArray();

            do {
                SecureRandom rnd = new SecureRandom();
                halfOfVektor = dataBlockSize / 2;

                do {
                    firstVektorBlock = new BigInteger(halfOfVektor * 8 - 1, rnd).toByteArray();
                    secVektorBlock = new BigInteger(halfOfVektor * 8 - 1, rnd).toByteArray();
                } while (firstVektorBlock.length != halfOfVektor || secVektorBlock.length != halfOfVektor);
                initVektorBlock = new byte[dataBlockSize];
                System.arraycopy(firstVektorBlock, 0, initVektorBlock, 0, halfOfVektor);
                System.arraycopy(secVektorBlock, 0, initVektorBlock, halfOfVektor, halfOfVektor);

                firstVektorBlock = encryptBlock(firstVektorBlock, n);
                secVektorBlock = encryptBlock(secVektorBlock, n);
            } while (firstVektorBlock.length != keySize || secVektorBlock.length != keySize);

            byte[] prevBlock = new byte[dataBlockSize];
            System.arraycopy(initVektorBlock, 0, prevBlock, 0, dataBlockSize);
            byte[] nextBlock = new byte[dataBlockSize];
            int j = 0;
            for (int i = 0; i < plainBlocksLen; i += plainBlockSize) {

                // Convert i to percent for ProgressBar
                percentProgress = (int) (((float) i / (float) plainBlocksLen) * 100);

                // Print ProgressBar
               // if (percentProgress > prevPercent) {
               //     CryptobyHelper.printProgressBar(percentProgress);
                //}
                prevPercent = percentProgress;
                // Copy Part of PlainInput in to Block
                System.arraycopy(plainInput, i, plainBlock, 0, plainBlockSize);

                // Add a One Byte into first Byte of Plain Array
                System.arraycopy(one, 0, plusOnePlainBlock, 0, one.length);
                System.arraycopy(plainBlock, 0, plusOnePlainBlock, 1, plainBlockSize);

                // Encrypt Block
                cryptBlock = encryptBlock(plusOnePlainBlock, n);

                // Copy Crypt Block in to extended DataBlock
                System.arraycopy(cryptBlock, 0, dataBlock, 0, cryptBlock.length);

                // Copy in last Byte of dataBlock the Size of cryptBlock
                cryptBlockSize = BigInteger.valueOf(cryptBlock.length - keySize).toByteArray();
                System.arraycopy(cryptBlockSize, 0, dataBlock, dataBlock.length - 1, cryptBlockSize.length);
                System.arraycopy(dataBlock, 0, nextBlock, 0, dataBlockSize);

                // XOR dataBlock with prevBlock
                dataBlock = CryptobyHelper.xorByteArrays(dataBlock, prevBlock);
                System.arraycopy(nextBlock, 0, prevBlock, 0, dataBlockSize);

                // Copy xored dataBlock to Output Array
                System.arraycopy(dataBlock, 0, cryptOutput, j, dataBlockSize);
                j += dataBlockSize;
            }
            if (rest != 0) {
                // crypt rest of PlainInput
                plainBlock = new byte[rest];
                plusOnePlainBlock = new byte[rest + 1];
                dataBlock = new byte[dataBlockSize];
                System.arraycopy(plainInput, plainBlocksLen, plainBlock, 0, plainBlock.length);

                // Add a One Byte in to first Byte of Plain Array
                System.arraycopy(one, 0, plusOnePlainBlock, 0, one.length);
                System.arraycopy(plainBlock, 0, plusOnePlainBlock, 1, plainBlock.length);

                // Encrypt rest of PlainInput
                cryptBlock = encryptBlock(plusOnePlainBlock, n);
                System.arraycopy(cryptBlock, 0, dataBlock, 0, cryptBlock.length);

                // Copy in last Byte of dataBlock the Size of cryptBlock
                cryptBlockSize = BigInteger.valueOf(cryptBlock.length - keySize).toByteArray();
                System.arraycopy(cryptBlockSize, 0, dataBlock, dataBlock.length - 1, 1);

                // XOR dataBlock with prevBlock
                dataBlock = CryptobyHelper.xorByteArrays(dataBlock, prevBlock);
                System.arraycopy(dataBlock, 0, cryptOutput, dataBlocksLen - 3 * dataBlockSize, dataBlockSize);
            } else {
                dataBlock = new byte[dataBlockSize];
                System.arraycopy(dataBlock, 0, cryptOutput, dataBlocksLen - 3 * dataBlockSize, dataBlockSize);
            }
            // Put crypted initVektor into last 2 Bytes of cryptOutput Array
            System.arraycopy(firstVektorBlock, 0, cryptOutput, dataBlocksLen - 2 * dataBlockSize, firstVektorBlock.length);
            System.arraycopy(secVektorBlock, 0, cryptOutput, dataBlocksLen - 1 * dataBlockSize, secVektorBlock.length);

        } else {
            cryptOutput = encryptBlock(plainInput, n);
        }
        //CryptobyHelper.printProgressBar(100);
        return cryptOutput;
    }