content: /**
   * Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * A copy of the License is located at
   *
   *     http://aws.amazon.com/apache2.0/
   *
   * or in the "license" file accompanying this file. This file is distributed
   * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
   * express or implied. See the License for the specific language governing
   * permissions and limitations under the License.
   */
  package com.amazon.utils.security;
  
  import com.amazon.android.utils.Helpers;
  
  import java.io.UnsupportedEncodingException;
  import java.security.InvalidAlgorithmParameterException;
  import java.security.InvalidKeyException;
  import java.security.Key;
  import java.security.NoSuchAlgorithmException;
  import java.security.spec.AlgorithmParameterSpec;
  
  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;
  
  /**
   * Utility class to Obfuscate a resource. The stream is first encrypted using the cipher algorithm
   * declared in the class and is then base64 encoded. The secret key and initialization vector are
   * generated by the utility itself, user has to provide random strings for key and Iv.
   */
  public class ResourceObfuscator {
  
      private static final String cipherAlgorithm = "AES/CBC/PKCS5Padding";
      private static final int CIPHER_KEY_LENGTH = 16;
      private static final int IV_LENGTH = 16;
  
      /**
       * Initializes the {@link Cipher}.
       *
       * @param mode                The mode.
       * @param randomStringsForKey The array of random strings used to generate secret key.
       * @param randomStringsForIv  The array of random strings used to generate Iv.
       * @return The {@link Cipher}.
       */
      private static Cipher initializeCipher(int mode, String[] randomStringsForKey, String[]
              randomStringsForIv) throws NoSuchAlgorithmException, NoSuchPaddingException,
              UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException {
  
          Cipher cipher = Cipher.getInstance(cipherAlgorithm);
          cipher.init(mode, generateSecretKey(randomStringsForKey), generateIv(randomStringsForIv));
          return cipher;
      }
  
      /**
       * Receives the plain resource via an input stream and returns an obfuscated form.
       *
       * @param plainString         Plain text to obfuscate.
       * @param randomStringsForKey Array of random strings used to generate secret key.
       * @param randomStringsForIv  Array of random strings used to generate Iv.
       * @return The obfuscated resource via an output stream.
       */
      public static String obfuscate(String plainString, String[]
              randomStringsForKey, String[] randomStringsForIv) throws InvalidKeyException,
              NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException,
              UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
  
          Cipher cipher = initializeCipher(Cipher.ENCRYPT_MODE, randomStringsForKey,
                                           randomStringsForIv);
          byte[] plainBytes = plainString.getBytes(Helpers.getDefaultAppCharset());
          byte[] encrypted = cipher.doFinal(plainBytes);
          return org.apache.commons.codec.binary.Base64.encodeBase64String(encrypted);
      }
  
      /**
       * Receives the obfuscated resource via an input stream and returns an unobfuscated/plain form.
       *
       * @param obfuscatedString    Obfuscated string to decrypt.
       * @param randomStringsForKey Array of random strings used to generate secret key.
       * @param randomStringsForIv  Array of random strings used to generate Iv.
       * @return The unobfuscated/plain resource via an output stream.
       */
      public static String unobfuscate(String obfuscatedString, String[]
              randomStringsForKey, String[] randomStringsForIv) throws InvalidKeyException,
              NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException,
              UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
  
          Cipher cipher = initializeCipher(Cipher.DECRYPT_MODE, randomStringsForKey,
                                           randomStringsForIv);
  
          byte[] base64Decoded = android.util.Base64.decode(obfuscatedString,
                                                            android.util.Base64.NO_WRAP);
  
          byte[] decrypted = cipher.doFinal(base64Decoded);
  
          return new String(decrypted, Helpers.getDefaultAppCharset());
  
      }
  
      /**
       * Generates a secret key using the array of random strings. It can be regenerated with the
       * same resources.
       *
       * @param randomStringsForKey Array of random strings.
       * @return The generated key.
       */
      private static Key generateSecretKey(String[] randomStringsForKey) throws
              UnsupportedEncodingException {
  
          String key = createString(randomStringsForKey);
          return new SecretKeySpec(key.getBytes(Helpers.getDefaultAppCharset()), "AES");
      }
  
      /**
       * Create a string using random strings.
       *
       * @param randomStrings The strings to use.
       * @return The string created using the random strings provided.
       */
      private static String createString(String[] randomStrings) {
  
          StringBuilder keyBuilder = new StringBuilder("");
          for (String randomString : randomStrings) {
              keyBuilder.append(randomString);
          }
          return generateSpecificLengthString(keyBuilder.toString());
      }
  
  
      /**
       * Generates an Iv using the array of random strings. It can be regenerated with the same
       * resources.
       *
       * @param randomStringsForIv Array of random strings.
       * @return The generated Iv.
       */
      private static AlgorithmParameterSpec generateIv(String[] randomStringsForIv) throws
              UnsupportedEncodingException {
  
          String iv = createString(randomStringsForIv);
          return new IvParameterSpec(iv.getBytes(Helpers.getDefaultAppCharset()));
      }
  
      /**
       * Converts the input string into a definite length string. The input string cannot be null.
       *
       * @param input The input string.
       * @return A string of a specific length.
       */
      private static String generateSpecificLengthString(String input) {
  
          if (input == null) {
              throw new RuntimeException("Input string cannot be null");
          }
          if (input.isEmpty()) {
              throw new RuntimeException("Input string cannot be empty");
          }
          String output = input;
          if (output.length() > CIPHER_KEY_LENGTH) {
              // Take the first CIPHER_KEY_LENGTH of characters.
              output = output.substring(0, CIPHER_KEY_LENGTH);
          }
          else if (output.length() < CIPHER_KEY_LENGTH) {
              // Keep appending the input string until the length crosses the desired length and then
              // truncate the string to achieve the desired length.
              while (output.length() < CIPHER_KEY_LENGTH) {
                  output = output + input;
              }
              if (output.length() > CIPHER_KEY_LENGTH) {
                  output = output.substring(0, CIPHER_KEY_LENGTH);
              }
          }
          return output;
      }
  }
  
  
  