Package org.keyczar.util

Source Code of org.keyczar.util.Util

/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 org.keyczar.util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import org.keyczar.exceptions.Base64DecodingException;
import org.keyczar.exceptions.KeyczarException;
import org.keyczar.interfaces.KeyType;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
* A miscellaneous utility class. Includes random number generation, int-to-byte
* conversion, etc.
*
* @author steveweis@gmail.com (Steve Weis)
*
*/
public class Util {
  private static final ConcurrentLinkedQueue<MessageDigest> DIGEST_QUEUE =
    new ConcurrentLinkedQueue<MessageDigest>();
  private static final ConcurrentLinkedQueue<SecureRandom> RAND_QUEUE =
    new ConcurrentLinkedQueue<SecureRandom>();
  private static final int READ_BUF_SIZE = 8192;

  private Util() {
    // Don't new me.
  }

  private static final ThreadLocal<Gson> GSON_THREAD_LOCAL = new ThreadLocal<Gson>() {
    @Override
    protected Gson initialValue() {
      return new GsonBuilder()
        .excludeFieldsWithoutExposeAnnotation()
        .registerTypeAdapter(KeyType.class, new KeyType.KeyTypeSerializer())
        .registerTypeAdapter(KeyType.class, new KeyType.KeyTypeDeserializer())
        .create();
    }
  };

  public static Gson gson() {
    return GSON_THREAD_LOCAL.get();
  }

  public static byte[] stripLeadingZeros(byte[] input) {
    int zeros = 0;

    // Find the first non-zero byte
    while (zeros < input.length && input[zeros] == 0) {
      zeros++;
    }

    if (zeros == 0) {
      return input;
    } else {
      byte[] output = new byte[input.length - zeros];
      System.arraycopy(input, zeros, output, 0, output.length);
      return output;
    }
  }

  /**
   * Returns a byte array containing 4 big-endian ordered bytes representing the
   * given integer.
   *
   * @param input The integer to convert to a byte array.
   * @return A byte array representation of an integer.
   */
  public static byte[] fromInt(int input) {
    byte[] output = new byte[4];
    writeInt(input, output, 0);
    return output;
  }

  /**
   * Returns a byte array containing 8 big-endian ordered bytes representing the
   * given long.
   *
   * @param input The long to convert to a byte array.
   * @return A byte array representation of a long.
   */
  public static byte[] fromLong(long input) {
    byte[] output = new byte[8];
    writeLong(input, output, 0);
    return output;
  }

  /**
   * Takes a variable number of byte arrays as input and hashes each one
   * prefixed by an integer representation of its size. For example,
   * prefixHash({0, 1, 2}, {1}) would hash the bytes equivalent to:
   * {3, 0, 1, 2, 1, 1}
   *
   * @param inputs The inputs to hash
   * @return The hash output
   * @throws KeyczarException If the SHA-1 algorithm is not found
   */
  public static byte[] prefixHash(byte[]... inputs) throws KeyczarException {
    MessageDigest md = DIGEST_QUEUE.poll();
    if (md == null) {
      try {
        md = MessageDigest.getInstance("SHA-1");
      } catch (NoSuchAlgorithmException e) {
        throw new KeyczarException(e);
      }
    }
    for (byte[] array : inputs) {
      md.update(fromInt(array.length));
      md.update(array);
    }
    byte[] digest = md.digest();
    DIGEST_QUEUE.add(md);
    return digest;
  }

  /**
   * Prefixes an input array with a 4-byte transportable length field.
   * If the input data is null or has zero length, returns a 4-byte
   * representation of 0.
   *
   * @param data
   * @return The input data prefixed by a 4-byte representation of its length
   */
  public static byte[] lenPrefix(byte[] data) {
    if (data == null || data.length == 0) {
      return fromInt(0);
    }
    return ByteBuffer.allocate(4 + data.length)
        .putInt(data.length)
        .put(data)
        .array();
  }

  /**
   * Packs a set of input arrays into a single array. The packed array is
   * prefixed by an integer value of the number of arrays. Then each individual
   * array is prefixed by its length, followed by the contents of the array
   * itself. Thus, three arrays A, B, C would output:
   *   {3, len(A), A, len(B), B, len(C), C}
   *
   * @param inputArrays A list of arrays to pack together
   * @return A packed list of arrays, with each preceded by its integer length
   */
  public static byte[] lenPrefixPack(byte[]... inputArrays) {
    // Count an int for each input array
    int outputSize = (1 + inputArrays.length) * 4;
    for (byte[] array : inputArrays) {
      outputSize += array.length;
    }
    byte[] output = new byte[outputSize];
    ByteBuffer outputBuffer = ByteBuffer.wrap(output);
    // Put the number of total arrays
    outputBuffer.putInt(inputArrays.length);
    for (byte[] array : inputArrays) {
      // Put the size of this array
      outputBuffer.putInt(array.length);
      // Put the array itself
      outputBuffer.put(array);
    }
    return output;
  }

  /**
   * Unpack an input buffer into an array of byte arrays
   *
   * @param packedInput A packed representation of an array of byte arrays
   * @param maxLength The max number of byte arrays allowed.
   * @return A two dimensional array of arrays, null if length prefix is not less than maxLength
   */
  public static byte[][] lenPrefixUnpack(byte[] packedInput, int maxArrays) {
    ByteBuffer input = ByteBuffer.wrap(packedInput);
    int numArrays = input.getInt();
    if (numArrays < 0 || numArrays > maxArrays) {
      return null;
    }
    byte[][] output = new byte[numArrays][];
    for (int i = 0; i < numArrays; i++) {
      if (input.remaining() < 4) {
        return null;
      }
      int len = input.getInt();
      if (len < 0 || len > input.remaining()) {
        return null;
      }
      byte[] array = new byte[len];
      input.get(array);
      output[i] = array;
    }
    return output;
  }

  /**
   * Hashes a variable number of byte arrays
   *
   * @param inputs The inputs to hash
   * @return The hash output
   * @throws KeyczarException If the SHA-1 algorithm is not found
   */
  public static byte[] hash(byte[]... inputs) throws KeyczarException {
    MessageDigest md = DIGEST_QUEUE.poll();
    if (md == null) {
      try {
        md = MessageDigest.getInstance("SHA-1");
      } catch (NoSuchAlgorithmException e) {
        throw new KeyczarException(e);
      }
    }
    for (byte[] array : inputs) {
      md.update(array);
    }
    byte[] digest = md.digest();
    DIGEST_QUEUE.add(md);
    return digest;
  }

  /**
   * Write random bytes into the destination. Uses pre-cached secure random
   * objects
   *
   * @param dest Destination to write the data
   */
  public static void rand(byte[] dest) {
    SecureRandom random = RAND_QUEUE.poll();
    if (random == null) {
      random = new SecureRandom();
    }
    random.nextBytes(dest);
    RAND_QUEUE.add(random);
  }

  /**
   * Returns an array of random bytes of the given length
   * @param len The length of the random array to output
   * @return A random array of bytes
   */
  public static byte[] rand(int len) {
    byte[] output = new byte[len];
    rand(output);
    return output;
  }

  /**
   * Reads 4 big-endian ordered bytes from a given offset in an array and
   * returns an integer representation.
   *
   * This method does not check the source array length.
   *
   * @param src The source array to read bytes from
   * @param offset The offset to start reading bytes from.
   * @return The integer value represented by the source array from the offset
   */
  static int readInt(byte[] src, int offset) {
    int output = 0;
    output |= (src[offset++] & 0xFF) << 24;
    output |= (src[offset++] & 0xFF) << 16;
    output |= (src[offset++] & 0xFF) << 8;
    output |= (src[offset++] & 0xFF);
    return output;
  }

  /**
   * Reads 8 big-endian ordered bytes from a given offset in an array and
   * returns a long representation.
   *
   * This method does not check the source array length.
   *
   * @param src The source array to read bytes from
   * @param offset The offset to start reading bytes from.
   * @return The long value represented by the source array from the offset
   */
  static long readLong(byte[] src, int offset) {
    long output = 0;
    output |= (src[offset++] & 0xFFL) << 56;
    output |= (src[offset++] & 0xFFL) << 48;
    output |= (src[offset++] & 0xFFL) << 40;
    output |= (src[offset++] & 0xFFL) << 32;
    output |= (src[offset++] & 0xFFL) << 24;
    output |= (src[offset++] & 0xFFL) << 16;
    output |= (src[offset++] & 0xFFL) << 8;
    output |= (src[offset++] & 0xFFL);
    return output;
  }

  /**
   * Converts a given byte array to an integer. Reads the bytes in big-endian
   * order.
   *
   * This method does not check the source array length.
   *
   * @param src A big-endian representation of an integer
   * @return The integer value represented by the source array
   */
  public static int toInt(byte[] src) {
    return readInt(src, 0);
  }

  /**
   * Converts a given byte array to a long. Reads the bytes in big-endian order.
   *
   * This method does not check the source array length.
   *
   * @param src A big-endian representation of a long
   * @return The long value represented by the source array
   */
  public static long toLong(byte[] src) {
    return readLong(src, 0);
  }

  /**
   * Writes 4 big-endian ordered bytes representing the given integer into the
   * destination byte array starting from the given offset.
   *
   * This method does not check the destination array length.
   *
   * @param input The integer to convert to bytes
   * @param dest The array in which to write the integer byte representation
   * @param offset The offset to start writing the bytes from
   */
  static void writeInt(int input, byte[] dest, int offset) {
    dest[offset++] = (byte) (input >> 24);
    dest[offset++] = (byte) (input >> 16);
    dest[offset++] = (byte) (input >> 8);
    dest[offset++] = (byte) (input);
  }

  /**
   * Writes 8 big-endian ordered bytes representing the given long into the
   * destination byte array starting from the given offset.
   *
   * This method does not check the destination array length.
   *
   * @param input The long to convert to bytes
   * @param dest The array in which to write the long byte representation
   * @param offset The offset to start writing the bytes from
   */
  static void writeLong(long input, byte[] dest, int offset) {
    dest[offset++] = (byte) (input >> 56);
    dest[offset++] = (byte) (input >> 48);
    dest[offset++] = (byte) (input >> 40);
    dest[offset++] = (byte) (input >> 32);
    dest[offset++] = (byte) (input >> 24);
    dest[offset++] = (byte) (input >> 16);
    dest[offset++] = (byte) (input >> 8);
    dest[offset++] = (byte) (input);
  }

  /**
   * An array comparison that is safe from timing attacks. If two arrays are
   * of equal length, this code will always check all elements, rather than
   * exiting once it encounters a differing byte.
   *
   * @param a1 An array to compare
   * @param a2 Another array to compare
   * @return True if these arrays are both null or if they have equal length
   *         and equal bytes in all elements
   */
  public static boolean safeArrayEquals(byte[] a1, byte[] a2) {
    if (a1 == null || a2 == null) {
        return (a1 == a2);
    }
    if (a1.length != a2.length) {
      return false;
    }
    byte result = 0;
    for (int i = 0; i < a1.length; i++) {
      result |= a1[i] ^ a2[i];
    }
    return (result == 0);
  }

  /**
   * Concatenate arrays together.
   *
   * @param arrays byte[] arrays to combine
   * @return single byte[] with all the data combined.
   */
  public static byte[] cat(byte[]... arrays) {
    int length = 0;
    for (byte[] array : arrays) {
      length += array.length;
    }
    byte[] result = new byte[length];
    int pos = 0;
    for (byte[] array : arrays) {
      System.arraycopy(array, 0, result, pos, array.length);
      pos += array.length;
    }
    return result;
  }

  /**
   * Splits a string into chunks of specified size.
   */
  public static List<String> split(String s, int chunkSize) {
    List<String> chunks = new ArrayList<String>();
    int length = s.length();
    for (int i = 0; i < length; i += chunkSize) {
      chunks.add(s.substring(i, Math.min(length, i + chunkSize)));
    }
    return chunks;
  }

  /**
   * Reads all data from the provided stream into memory and returns it as a byte array.
   */
  public static byte[] readStreamFully(InputStream inStream) throws IOException {
    ByteArrayOutputStream tempStream = new ByteArrayOutputStream();
    byte[] buf = new byte[Util.READ_BUF_SIZE];
    int bytesRead = 0;
    while ((bytesRead = inStream.read(buf)) != -1) {
      tempStream.write(buf, 0, bytesRead);
    }
    return tempStream.toByteArray();
  }

  /**
   * Web safe Base64-encode a BigInteger.
   */
  public static String encodeBigInteger(BigInteger value) {
    return Base64Coder.encodeWebSafe(value.toByteArray());
  }

  /**
   * Web safe Base64-decode a BigInteger.
   */
  public static BigInteger decodeBigInteger(String value) throws Base64DecodingException {
    return new BigInteger(Base64Coder.decodeWebSafe(value));
  }

  /**
   * Generate a public/private key pair with the specified algorithm and key size.
   */
  public static KeyPair generateKeyPair(String algorithm, int keySize)
      throws KeyczarException {
    try {
      KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm);
      kpg.initialize(keySize);
      KeyPair pair = kpg.generateKeyPair();
      return pair;
    } catch (GeneralSecurityException e) {
      throw new KeyczarException(e);
    }
  }
}
TOP

Related Classes of org.keyczar.util.Util

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.