Package org.keyczar

Source Code of org.keyczar.AesKey

/*
* 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;

import com.google.gson.annotations.Expose;

import org.keyczar.enums.CipherMode;
import org.keyczar.exceptions.KeyczarException;
import org.keyczar.exceptions.ShortBufferException;
import org.keyczar.i18n.Messages;
import org.keyczar.interfaces.DecryptingStream;
import org.keyczar.interfaces.EncryptingStream;
import org.keyczar.interfaces.KeyType;
import org.keyczar.interfaces.SigningStream;
import org.keyczar.interfaces.Stream;
import org.keyczar.interfaces.VerifyingStream;
import org.keyczar.keyparams.AesKeyParameters;
import org.keyczar.util.Base64Coder;
import org.keyczar.util.Util;

import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;

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

/**
* Wrapping class for AES keys. Currently the default is to use CBC mode.
*
* @author steveweis@gmail.com (Steve Weis)
* @author arkajit.dey@gmail.com (Arkajit Dey)
*
*/
public class AesKey extends KeyczarKey {
  private static final DefaultKeyType KEY_TYPE = DefaultKeyType.AES;
  private static final int BLOCK_SIZE = 16;
  private static final String AES_ALGORITHM = "AES";
  private static final CipherMode DEFAULT_MODE = CipherMode.CBC;
  private static final int NUM_OF_KEYS = 2; // AesKeys contain HMAC and AES keys

  private SecretKey aesKey;
  @Expose private final String aesKeyString;
  @Expose private final HmacKey hmacKey;
  @Expose private final CipherMode mode;

  private final byte[] hash = new byte[Keyczar.KEY_HASH_SIZE];

  /**
   * Creates an AES key from the provided key data and HMAC key.  The key data can be any
   * byte array, but must be a valid AES key length (128, 192 or 256 bits).
   */
  public AesKey(byte[] aesKeyBytes, HmacKey hmacKey) throws KeyczarException {
    super(aesKeyBytes.length * 8);
    this.aesKeyString = Base64Coder.encodeWebSafe(aesKeyBytes);
    this.mode = DEFAULT_MODE;
    this.hmacKey = hmacKey;
    initJceKey(aesKeyBytes);
  }

  // Used by GSON, which will overwrite the values set here.
  private AesKey() {
    super(0);
    aesKeyString = null;
    hmacKey = null;
    mode = null;
  }

  static AesKey generate(AesKeyParameters params) throws KeyczarException {
    return new AesKey(Util.rand(params.getKeySize() / 8), params.getHmacKey());
  }

  /*
   * Used by SessionDecrypters when decrypting encrypted keys
   */
  static AesKey fromPackedKey(byte[] packedKeys) throws KeyczarException {
    byte[][] unpackedKeys = Util.lenPrefixUnpack(packedKeys, NUM_OF_KEYS);
    if (unpackedKeys == null || unpackedKeys.length != NUM_OF_KEYS) {
      throw new KeyczarException(Messages.getString("AesKey.InvalidPackedKey"));
    }
    return new AesKey(unpackedKeys[0], new HmacKey(unpackedKeys[1]));
  }

  @Override
  public KeyType getType() {
    return KEY_TYPE;
  }

  @Override
  protected byte[] hash() {
    return hash;
  }

  static AesKey read(String input) throws KeyczarException {
    AesKey key = Util.gson().fromJson(input, AesKey.class);
    key.hmacKey.initFromJson();
    key.initJceKey(Base64Coder.decodeWebSafe(key.aesKeyString));
    return key;
  }

  private void initJceKey(byte[] aesBytes) throws KeyczarException {
    aesKey = new SecretKeySpec(aesBytes, AES_ALGORITHM);
    byte[] fullHash = Util.hash(Util.fromInt(BLOCK_SIZE), aesBytes, hmacKey.getEncoded());
    System.arraycopy(fullHash, 0, hash, 0, hash.length);
  }

  /*
   * Used by SessionEncrypters to get a packed representation of an AES and
   * HMAC key.
   */
  byte[] getEncoded() {
    return Util.lenPrefixPack(aesKey.getEncoded(), hmacKey.getEncoded());
  }

  @Override
  protected Stream getStream() throws KeyczarException {
    return new AesStream();
  }

  @Override
  protected SecretKey getJceKey() {
    return aesKey;
  }

  private class AesStream implements EncryptingStream, DecryptingStream {
    private final Cipher encryptingCipher;
    private final Cipher decryptingCipher;
    private final SigningStream signStream;
    boolean ivRead = false;

    public AesStream() throws KeyczarException  {
      /*
       * The JCE Cipher.init() call essentially reallocates a new Cipher object
       * We avoid this by initializing two Cipher objects with zero-valued IVs,
       * Then passing IVs for CBC mode ourselves. The Ciphers will be cached in
       * this stream
       */
      IvParameterSpec zeroIv = new IvParameterSpec(new byte[BLOCK_SIZE]);
      try {
        encryptingCipher = Cipher.getInstance(mode.getMode());
        encryptingCipher.init(Cipher.ENCRYPT_MODE, aesKey, zeroIv);
        decryptingCipher = Cipher.getInstance(mode.getMode());
        decryptingCipher.init(Cipher.DECRYPT_MODE, aesKey, zeroIv);
        signStream = (SigningStream) hmacKey.getStream();
      } catch (GeneralSecurityException e) {
        throw new KeyczarException(e);
      }
    }

    @Override
    public SigningStream getSigningStream() {
      return signStream;
    }

    @Override
    public VerifyingStream getVerifyingStream() {
      return (VerifyingStream) signStream;
    }

    @Override
    public void initDecrypt(ByteBuffer input) {
      // This will simply decrypt the first block, leaving the CBC Cipher
      // ready for the next block of input.
      byte[] iv = new byte[BLOCK_SIZE];
      input.get(iv);
      decryptingCipher.update(iv);
      ivRead = true;
    }

    @Override
    public int initEncrypt(ByteBuffer output) throws KeyczarException {
      // Generate a random value and encrypt it. This will be the IV.
      byte[] ivPreImage = new byte[BLOCK_SIZE];
      Util.rand(ivPreImage);
      try {
        return encryptingCipher.update(ByteBuffer.wrap(ivPreImage), output);
      } catch (javax.crypto.ShortBufferException e) {
        throw new ShortBufferException(e);
      }
    }

    @Override
    public int updateDecrypt(ByteBuffer input, ByteBuffer output)
        throws KeyczarException {
      if (ivRead && input.remaining() >= BLOCK_SIZE) {
        // The next output block will be the IV preimage, which we'll discard
        byte[] temp = new byte[BLOCK_SIZE];
        input.get(temp);
        decryptingCipher.update(temp)// discard IV preimage byte array
        ivRead = false;
      }
      try {
        return decryptingCipher.update(input, output);
      } catch (javax.crypto.ShortBufferException e) {
        throw new ShortBufferException(e);
      }
    }

    @Override
    public int updateEncrypt(ByteBuffer input, ByteBuffer output)
        throws KeyczarException {
      try {
        return encryptingCipher.update(input, output);
      } catch (javax.crypto.ShortBufferException e) {
        throw new ShortBufferException(e);
      }
    }

    @Override
    public int doFinalDecrypt(ByteBuffer input, ByteBuffer output)
        throws KeyczarException {
      if (ivRead) {
        if (input.remaining() == 0) {
          // This can occur if someone encrypts an 0-length array
          return 0;
        }
        // The next output block will be the IV preimage, which we'll discard
        byte[] temp = new byte[BLOCK_SIZE];
        input.get(temp);
        decryptingCipher.update(temp)// discard IV preimage byte array
        ivRead = false;
      }
      try {
        if (input.remaining() == 0) {
          byte[] outputBytes = decryptingCipher.doFinal();
          output.put(outputBytes);
          return outputBytes.length;
        } else {
          return decryptingCipher.doFinal(input, output);
        }
      } catch (GeneralSecurityException e) {
        throw new KeyczarException(e);
      }
    }

    @Override
    public int doFinalEncrypt(ByteBuffer input, ByteBuffer output)
        throws KeyczarException {
      try {
        return encryptingCipher.doFinal(input, output);
      } catch (GeneralSecurityException e) {
        throw new KeyczarException(e);
      }
    }

    @Override
    public int maxOutputSize(int inputLen) {
      return mode.getOutputSize(BLOCK_SIZE, inputLen);
    }
  }
}
TOP

Related Classes of org.keyczar.AesKey

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.