All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.shindig.common.crypto.Crypto Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.shindig.common.crypto;

import com.google.common.primitives.Bytes;
import org.apache.commons.codec.binary.Hex;

import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * Cryptographic utility functions.
 */
public final class Crypto {
  
  /** 
   * Use this random number generator instead of creating your own.  This is
   * thread-safe.
   */
  public static final SecureRandom RAND = new SecureRandom();
  
  /**
   * HMAC algorithm to use
   */
  private final static String HMAC_TYPE = "HMACSHA1";
  
  /** 
   * minimum safe length for hmac keys (this is good practice, but not 
   * actually a requirement of the algorithm
   */
  private final static int MIN_HMAC_KEY_LEN = 8;
  
  /**
   * Encryption algorithm to use
   */
  private final static String CIPHER_TYPE = "AES/CBC/PKCS5Padding";
  
  private final static String CIPHER_KEY_TYPE = "AES";
  
  /**
   * Use keys of this length for encryption operations
   */
  public final static int CIPHER_KEY_LEN = 16;
  
  private static final int CIPHER_BLOCK_SIZE = 16;
  
  /**
   * Length of HMAC SHA1 output
   */
  public final static int HMAC_SHA1_LEN = 20;

  private final static char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
  
  // everything is static, no instantiating this class
  private Crypto() { 
  }

  /**
   * Gets a hex encoded random string.
   * 
   * @param numBytes number of bytes of randomness.
   */
  public static String getRandomString(int numBytes) {
    return new String(Hex.encodeHex(getRandomBytes(numBytes)));
  }

  /**
   * @return a random string of digits of the specified length.
   */
  public static String getRandomDigits(int len) {
    byte[] random = getRandomBytes(len);
    StringBuilder out = new StringBuilder(len);
    for (int i = 0; i < len; ++i) {
      out.append(DIGITS[Math.abs(random[i] % DIGITS.length)]);
    }
    return out.toString();
  }
  
  /**
   * Returns strong random bytes.
   * 
   * @param numBytes number of bytes of randomness
   */
  public static byte[] getRandomBytes(int numBytes) {
    byte[] out = new byte[numBytes];
    RAND.nextBytes(out);
    return out;
  }
  
  /**
   * HMAC sha1
   * 
   * @param key the key must be at least 8 bytes in length.
   * @param in byte array to HMAC.
   * @return the hash
   * 
   * @throws GeneralSecurityException
   */
  public static byte[] hmacSha1(byte[] key, byte[] in) throws GeneralSecurityException {
    if (key.length < MIN_HMAC_KEY_LEN) {
      throw new GeneralSecurityException("HMAC key should be at least "
          + MIN_HMAC_KEY_LEN + " bytes.");
    }
    Mac hmac = Mac.getInstance(HMAC_TYPE);
    Key hmacKey = new SecretKeySpec(key, HMAC_TYPE);
    hmac.init(hmacKey);
    hmac.update(in);
    return hmac.doFinal();
  }
  
  /**
   * Verifies an HMAC SHA1 hash.  Throws if the verification fails.
   * 
   * @param key
   * @param in
   * @param expected
   * @throws GeneralSecurityException
   */
  public static void hmacSha1Verify(byte[] key, byte[] in, byte[] expected)
  throws GeneralSecurityException {
    Mac hmac = Mac.getInstance(HMAC_TYPE);
    Key hmacKey = new SecretKeySpec(key, HMAC_TYPE);
    hmac.init(hmacKey);
    hmac.update(in);
    byte actual[] = hmac.doFinal();
    if (actual.length != expected.length) {
      throw new GeneralSecurityException("HMAC verification failure");
    }
    for (int i=0; i < actual.length; i++) {
      if (actual[i] != expected[i]) {
        throw new GeneralSecurityException("HMAC verification failure");        
      }
    }
  }
  
  /**
   * AES-128-CBC encryption.  The IV is returned as the first 16 bytes
   * of the cipher text.
   * 
   * @param key
   * @param plain
   * 
   * @return the IV and cipher text
   * 
   * @throws GeneralSecurityException
   */
  public static byte[] aes128cbcEncrypt(byte[] key, byte[] plain)
  throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
    byte iv[] = getRandomBytes(cipher.getBlockSize());
    return Bytes.concat(iv, aes128cbcEncryptWithIV(key, iv, plain));
  }

  /**
   * AES-128-CBC encryption with a given IV.
   *
   * @param key
   * @param iv
   * @param plain
   *
   * @return the cipher text
   *
   * @throws GeneralSecurityException
   */
  public static byte[] aes128cbcEncryptWithIV(byte[] key, byte[] iv, byte[] plain)
  throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
    Key cipherKey = new SecretKeySpec(key, CIPHER_KEY_TYPE);
    IvParameterSpec ivSpec = new IvParameterSpec(iv);
    cipher.init(Cipher.ENCRYPT_MODE, cipherKey, ivSpec);
    return cipher.doFinal(plain);
  }


  /**
   * AES-128-CBC decryption.  The IV is assumed to be the first 16 bytes
   * of the cipher text.
   * 
   * @param key
   * @param cipherText
   * 
   * @return the plain text
   * 
   * @throws GeneralSecurityException
   */
  public static byte[] aes128cbcDecrypt(byte[] key, byte[] cipherText)
  throws GeneralSecurityException {
    byte iv[] = new byte[CIPHER_BLOCK_SIZE];
    System.arraycopy(cipherText, 0, iv, 0, iv.length);
    return aes128cbcDecryptWithIv(key, iv, cipherText, iv.length);
  }
  
  /**
   * AES-128-CBC decryption with a particular IV.
   * 
   * @param key decryption key
   * @param iv initial vector for decryption
   * @param cipherText cipher text to decrypt
   * @param offset offset into cipher text to begin decryption
   * 
   * @return the plain text
   * 
   * @throws GeneralSecurityException
   */
  public static byte[] aes128cbcDecryptWithIv(byte[] key, byte[] iv,
      byte[] cipherText, int offset) throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
    Key cipherKey = new SecretKeySpec(key, CIPHER_KEY_TYPE);
    IvParameterSpec ivSpec = new IvParameterSpec(iv);
    cipher.init(Cipher.DECRYPT_MODE, cipherKey, ivSpec);
    return cipher.doFinal(cipherText, offset, cipherText.length-offset);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy