gt:                     lb = this.cipher.doFinal(data, data.length - 16, 16);
context: /**
   * Constellio, Open Source Enterprise Search
   * Copyright (C) 2010 DocuLibre inc.
   *
   * This copyrighted material is made available to anyone wishing to use, modify,
   * copy, or redistribute it subject to the terms and conditions of the GNU
   * Lesser General Public License, as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   * for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with this distribution; if not, write to:
   * Free Software Foundation, Inc.
   * 51 Franklin Street, Fifth Floor
   * Boston, MA  02110-1301  USA
   */
  package com.doculibre.constellio.utils.aes.lslAESCrypto;
   
  import java.security.InvalidAlgorithmParameterException;
  import java.security.InvalidKeyException;
  import java.security.NoSuchAlgorithmException;
  import java.security.SecureRandom;
  import java.util.Random;
  
  import javax.crypto.BadPaddingException;
  import javax.crypto.Cipher;
  import javax.crypto.IllegalBlockSizeException;
  import javax.crypto.NoSuchPaddingException;
  import javax.crypto.spec.IvParameterSpec;
  import javax.crypto.spec.SecretKeySpec;
   
  /**
   * <p>
   * This is a class designed to process AES messages sent from the LSL
   * implementation of AES which can be found here:<br/>
   * <a
   * href="https://wiki.secondlife.com/wiki/AES_LSL_Implementation">https://wiki
   * .secondlife.com/wiki/AES_LSL_Implementation</a>
   * </p>
   * <p>
   * This Java class will be updated to support the same modes of operation as the
   * LSL implementation. It currently assumes that keys and input-vectors are
   * processed as hex-strings, and that text is received as plain-text, while
   * ciphertext will be handled as base64 strings.
   * </p>
   * 
   * @author Haravikk
   * @date Sep 15, 2008, 4:18:48 PM
   * @version 1.0
   */
  public class LSLAESCrypto {
      /** Our currently set block-cipher mode */
      protected LSLAESCryptoMode mode = LSLAESCryptoMode.CBC;
      /** Used to detect when a new {@link Cipher} Is needed. */
      protected boolean modeChanged = false;
   
      /** Our currently set padding mode */
      protected LSLAESCryptoPad pad = LSLAESCryptoPad.NONE;
      /** Our currently set pad-size */
      protected int padSize = 512;
   
      /** The currently loaded key */
      protected SecretKeySpec keySpec = null;
      /** The currently loaded input-vector */
      protected IvParameterSpec ivSpec = null;
   
      /** The currently active cipher */
      protected Cipher cipher = null;
   
      /** A random class for secure random operations. */
      protected Random random = new SecureRandom();
   
      /**
       * Creates an instance of an LSL compatible AES handler.
       * 
       * @param mode
       *            the cipher-block mode of operation
       * @param pad
       *            the padding scheme to use
       * @param padSize
       *            the block-size to use when padding. Must be a non-zero,
       *            positive value that is a multiple of 128.
       * @param hexKey
       *            the key to start with (represented as hexadecimal string)
       * @param hexIV
       *            the input vector to start with (represented as hexadecimal
       *            string)
       * @throws NoSuchAlgorithmException
       *             if the AES algorithm is not supported by the current JVM
       * @throws NoSuchPaddingException
       *             if the padding scheme chosen is not supported by the current
       *             JVM
       */
      public LSLAESCrypto(
          final LSLAESCryptoMode mode,
              final LSLAESCryptoPad pad,
              final int padSize,
              final String hexKey,
              final String hexIV)
          throws NoSuchAlgorithmException,
              NoSuchPaddingException {
          this.init(mode, pad, padSize, hexKey, hexIV);
      }
   
      /**
       * Decrypts a base64 ciphertext into plain-text
       * 
       * @param base64ciphertext
       *            the ciphertext to decrypt
       * @return the plain-text that was originally encrypted
       * @throws InvalidKeyException
       *             if the currently loaded key is not valid
       * @throws InvalidAlgorithmParameterException
       *             if the AES algorithm is not supported by the current JVM
       * @throws IllegalBlockSizeException
       *             if the ciphertext is somehow unreadable (bad base64
       *             conversion)
       * @throws BadPaddingException
       *             if the chosen mode of operation requires padded data
       */
      public String decrypt(final String base64ciphertext)
          throws InvalidKeyException,
              InvalidAlgorithmParameterException,
              IllegalBlockSizeException,
              BadPaddingException {
          if (this.modeChanged) try {
              this.createCipher();
          } catch (final Exception e) { /* Do nothing */}
   
          this.cipher.init(Cipher.DECRYPT_MODE, this.keySpec, this.ivSpec);
          return new String(this.cipher.doFinal(Base64Coder
              .decode(base64ciphertext)));
      }
   
      /**
       * Encrypts plain-text into a base64 string
       * 
       * @param text
       *            the plain-text to encrypt
       * @return the base64 ciphertext produced
       * @throws IllegalBlockSizeException
       *             if the plain text is somehow invalid
       * @throws BadPaddingException
       *             if the chosen mode of operation requires padded data
       * @throws InvalidKeyException
       *             if the currently loaded key is invalid
       * @throws InvalidAlgorithmParameterException
       *             if the AES algorithm is not supported by the current JVM
       */
      public String encrypt(final String text)
          throws IllegalBlockSizeException,
              BadPaddingException,
              InvalidKeyException,
              InvalidAlgorithmParameterException {
          if (this.modeChanged) try {
              this.createCipher();
          } catch (final Exception e) { /* Do nothing */}
   
          this.cipher.init(Cipher.ENCRYPT_MODE, this.keySpec, this.ivSpec);
   
          byte[] data = text.getBytes();
          int bits = data.length * 8;
   
          /* Apply padding */
          LSLAESCryptoPad padding = this.pad;
          if (padding == LSLAESCryptoPad.NONE) {
              if (this.mode == LSLAESCryptoMode.CFB) { return Base64Coder
                  .encodeString(this.cipher.doFinal(data), 0, bits); }
              padding = LSLAESCryptoPad.RBT;
          }
   
          int blockSize = this.padSize;
          if (padding == LSLAESCryptoPad.RBT) blockSize = 128;
   
          final int blocks = bits / blockSize;
          int extra = bits % blockSize;
   
          if (padding == LSLAESCryptoPad.RBT) {
              if (extra > 0) {
                  /*
                   * This scheme takes the last encrypted block, encrypts it
                   * again, and XORs it with any leftover data, maintaining
                   * data-length. If input is less than a block in size, then the
                   * current input-vector is used.
                   */
                  int bytes = extra / 8;
                  if ((bytes * 8) < extra) ++bytes;
   
                  // Grab leftover bytes
                  final byte[] t = new byte[bytes];
                  if (bytes > 0)
                      System.arraycopy(data, data.length - bytes, t, 0, bytes);
   
                  // Encrypt all other data.
                  byte[] lb;
                  if (blocks < 1) {
                      // If not enough for a block, double-encrypt IV.
                      data = new byte[0];
                      lb =
                          this.cipher.doFinal(this.cipher.doFinal(this.ivSpec
                              .getIV()));
                  } else {
                      // If there are blocks, then double-encrypt final one.
                      data = this.cipher.doFinal(data, 0, data.length - bytes);
  