Java tutorial
/* * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.common.security.crypto; import java.io.IOException; import java.io.OutputStream; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.spec.InvalidKeySpecException; import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import mitm.common.security.NoSuchProviderRuntimeException; import mitm.common.security.SecurityFactory; import mitm.common.security.SecurityFactoryFactory; import mitm.common.util.Check; import org.apache.commons.lang.ArrayUtils; /** * OutputStream that must be wrapped around another OutputStream so the output can be encrypted. * * This class is not thread safe. * * @author Martijn Brinkers * */ public class PBEncryptionOutputStream extends OutputStream { private final static String DEFAULT_ALGORITHM = "PBEWITHSHA256AND128BITAES-CBC-BC"; /* * larger iteration count improves security because "brute force" password cracking takes * longer. Disadvantage, naturally, is that it takes longer to decrypt the key. */ private final static int DEFAULT_ITERATION_COUNT = 2048; /* * default salt length in bytes (16 = 128 bits/8) */ private final static int DEFAULT_SALT_LENGTH = 16; /* * Encrypted content will be delegated to this OutputStream */ private final OutputStream delegate; /* * How many times the password will be hashed (more times is more secure but slower) */ private final int iterationCount; /* * The length of the salt (in bytes) for the randomly generated salt */ private final int saltLength; /* * The encryption algorithm to be used */ private final String algorithm; /* * The password which will be used to encrypt the data with. */ private final char[] password; /* * The randomly generated salt */ private byte[] salt; /* * The cipher used for encrypting the data (the cipher is based on algorithm). */ private Cipher cipher; /* * True if the encryption parameters are written to the stream */ boolean parametersWritten; /* * True if Stream is closed */ boolean closed; public PBEncryptionOutputStream(OutputStream delegate, int iterationCount, int saltLength, String algorithm, char[] password) throws CryptoException { Check.notNull(delegate, "delegate"); Check.notNull(algorithm, "algorithm"); Check.notNull(password, "password"); this.delegate = delegate; this.iterationCount = iterationCount; this.saltLength = saltLength; this.algorithm = algorithm; this.password = ArrayUtils.clone(password); init(); } public PBEncryptionOutputStream(OutputStream delegate, char[] password) throws CryptoException { this(delegate, DEFAULT_ITERATION_COUNT, DEFAULT_SALT_LENGTH, DEFAULT_ALGORITHM, password); } private void init() throws CryptoException { try { SecurityFactory securityFactory = SecurityFactoryFactory.getSecurityFactory(); SecretKeyFactory keyFactory = securityFactory.createSecretKeyFactory(algorithm); RandomGenerator randomGenerator = securityFactory.createRandomGenerator(); salt = randomGenerator.generateRandom(saltLength); PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount); /* * Clear out the password */ Arrays.fill(password, '#'); Key secretKey = keyFactory.generateSecret(keySpec); cipher = securityFactory.createCipher(algorithm); cipher.init(Cipher.ENCRYPT_MODE, secretKey); } catch (NoSuchProviderException e) { throw new NoSuchProviderRuntimeException(e); } catch (NoSuchAlgorithmException e) { throw new CryptoException(e); } catch (InvalidKeySpecException e) { throw new CryptoException(e); } catch (NoSuchPaddingException e) { throw new CryptoException(e); } catch (InvalidKeyException e) { throw new CryptoException(e); } } private void writeParameters() throws IOException { if (!parametersWritten) { PBEncryptedStreamParameters parameters = new PBEncryptedStreamParameters(algorithm, salt, iterationCount); delegate.write(parameters.getEncoded()); parametersWritten = true; } } private void checkClosed() throws IOException { if (closed) { throw new IOException("OutputStream is closed."); } } @Override public void write(int i) throws IOException { byte[] b = new byte[] { (byte) i }; write(b); } @Override public void write(byte data[]) throws IOException { write(data, 0, data.length); } @Override public void write(byte data[], int offset, int length) throws IOException { checkClosed(); writeParameters(); byte[] encrypted = cipher.update(data, offset, length); if (encrypted != null) { delegate.write(encrypted); } } @Override public void flush() throws IOException { checkClosed(); delegate.flush(); } @Override public void close() throws IOException { if (!closed) { try { writeParameters(); byte[] finalBytes = cipher.doFinal(); delegate.write(finalBytes); delegate.close(); closed = true; } catch (IllegalBlockSizeException e) { throw new IOException(e); } catch (BadPaddingException e) { throw new IOException(e); } } } }