org.sperle.keepass.crypto.bc.AESCipher.java Source code

Java tutorial

Introduction

Here is the source code for org.sperle.keepass.crypto.bc.AESCipher.java

Source

/*
Copyright (c) 2009-2010 Christoph Sperle <keepassmobile@gmail.com>
    
This file is part of KeePassMobile.
    
KeePassMobile is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
KeePassMobile is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with KeePassMobile.  If not, see <http://www.gnu.org/licenses/>.
    
*/

package org.sperle.keepass.crypto.bc;

import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.sperle.keepass.crypto.KdbCipher;
import org.sperle.keepass.crypto.KeePassCryptoException;
import org.sperle.keepass.monitor.ProgressMonitor;
import org.sperle.keepass.util.ByteArrays;

/**
 * The AES (Rijndael) cipher using bouncy castle to encrypt/decrypt the KeePassDB.
 */
public final class AESCipher implements KdbCipher {
    public static final String NAME = "AES";

    public String getName() {
        return NAME;
    }

    public byte[] encrypt(byte[] key, byte[] plainText, byte[] iv, int rounds, boolean padding, ProgressMonitor pm)
            throws KeePassCryptoException {
        try {
            BufferedBlockCipher cipher = null;
            if (padding) {
                cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
            } else {
                cipher = new BufferedBlockCipher(new AESEngine());
            }

            if (iv != null)
                cipher.init(true, new ParametersWithIV(new KeyParameter(key), iv));
            else
                cipher.init(true, new KeyParameter(key));

            if (pm != null) {
                if (rounds == 1)
                    pm.nextStep(plainText.length / cipher.getBlockSize(), "pm_encrypt"); // count length (database)
                else if (rounds > 1)
                    pm.nextStep(rounds, "pm_encrypt"); // count rounds (master password)
            }

            byte[] cipherText = null;
            if (padding) {
                cipherText = new byte[cipher.getOutputSize(plainText.length)];
            } else {
                cipherText = new byte[plainText.length];
            }

            int outLength = cipher.processBytes(plainText, 0, plainText.length, cipherText, 0,
                    rounds == 1 ? pm : null);
            if (outLength == -1)
                return null; // user canceled
            if (rounds > 1) {
                if (pm != null)
                    pm.tick();
                for (int i = 1; i < rounds; i++) {
                    outLength = cipher.processBytes(cipherText, 0, cipherText.length, cipherText, 0, null);
                    if (pm != null) {
                        if (pm.isCanceled())
                            return null;
                        pm.tick();
                    }
                }
            }

            if (padding)
                cipher.doFinal(cipherText, outLength);
            return cipherText;
        } catch (Exception e) {
            throw new KeePassCryptoException("Exception during AES encryption: " + e.getMessage());
        }
    }

    public byte[] decrypt(byte[] key, byte[] cipherText, byte[] iv, ProgressMonitor pm)
            throws KeePassCryptoException {
        try {
            BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
            if (iv != null)
                cipher.init(false, new ParametersWithIV(new KeyParameter(key), iv));
            else
                cipher.init(false, new KeyParameter(key));
            if (pm != null)
                pm.nextStep(cipherText.length / cipher.getBlockSize(), "pm_decrypt");
            byte[] plainText = new byte[cipher.getOutputSize(cipherText.length)];
            int outLength = cipher.processBytes(cipherText, 0, cipherText.length, plainText, 0, pm);
            if (outLength == -1)
                return null; // user canceled
            outLength += cipher.doFinal(plainText, outLength);
            return (outLength < plainText.length) ? ByteArrays.cut(plainText, outLength) : plainText;
        } catch (Exception e) {
            throw new KeePassCryptoException("Exception during AES decryption: " + e.getMessage());
        }
    }
}