/* * Copyright 2014-2015 Qianqian Zhu <zhuqianqian.299@gmail.com> 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. * 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 com.z299studio.pb; import android.util.Log; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; class Crypto { private static final String PBKDF2_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA1"; private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; private static int KEY_LENGTH = 256; static final int SALT_LENGTH = KEY_LENGTH / 8; private static SecureRandom random = new SecureRandom(); private byte [] mSalt = null; private byte [] mIv = null; private SecretKey mKey = null; private int mIterationCount; Crypto () { mIterationCount = 800; } int getIterationCount() { return mIterationCount; } int getIvLength() {return mIv.length; } // Reset or set the password first time should call this. void resetPassword(String password) { mSalt = new byte[SALT_LENGTH]; random.nextBytes(mSalt); deriveKey(password); } private void deriveKey(String password) { byte[] keyBytes; KeySpec keySpec = new PBEKeySpec(password.toCharArray(), mSalt, mIterationCount, KEY_LENGTH); SecretKeyFactory keyFactory; try { keyFactory = SecretKeyFactory .getInstance(PBKDF2_DERIVATION_ALGORITHM); keyBytes = keyFactory.generateSecret(keySpec).getEncoded(); mKey = new SecretKeySpec(keyBytes, "AES"); } catch (NoSuchAlgorithmException e) { Log.e("PwdBook:Crypto", "No encryption algorithm found"); } catch (InvalidKeySpecException e) { Log.e("PwdBook:Crypto", "Invalid key"); } } byte[] encrypt(byte [] data) { byte cipherData[]; if(mKey == null) { return null; } try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); mIv = new byte[cipher.getBlockSize()]; random.nextBytes(mIv); IvParameterSpec ivParams = new IvParameterSpec(mIv); cipher.init(Cipher.ENCRYPT_MODE, mKey, ivParams); cipherData = cipher.doFinal(data); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } return cipherData; } byte[] decrypt(byte [] data) throws GeneralSecurityException { byte plainData[]; if(mKey == null ) { return null; } Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); IvParameterSpec ivParams = new IvParameterSpec(mIv); cipher.init(Cipher.DECRYPT_MODE, mKey, ivParams); plainData = cipher.doFinal(data); return plainData; } byte[] getSaltAndIvBytes() { byte[] saltAndIv = new byte[mSalt.length + mIv.length]; System.arraycopy(mSalt, 0, saltAndIv, 0, mSalt.length); System.arraycopy(mIv, 0, saltAndIv, mSalt.length, mIv.length); return saltAndIv; } void setPassword(String password, byte[] saltAndIvData, int offset, int total) { mSalt = new byte[SALT_LENGTH]; mIv = new byte[total - SALT_LENGTH]; System.arraycopy(saltAndIvData, offset, mSalt, 0, SALT_LENGTH); System.arraycopy(saltAndIvData, offset+SALT_LENGTH, mIv, 0, mIv.length); deriveKey(password); } }