/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.core.internal.security;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_ALGORITHM_NAME;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_ITERATION_COUNT;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_KEY_DERIVATION_NAME;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_SALT;
import static org.xmind.core.internal.dom.DOMConstants.TAG_ALGORITHM;
import static org.xmind.core.internal.dom.DOMConstants.TAG_KEY_DERIVATION;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.xmind.core.Core;
import org.xmind.core.CoreException;
import org.xmind.core.IEncryptionData;
import org.xmind.core.IFileEntry;
import org.xmind.core.internal.zip.ArchiveConstants;
/**
* @author Frank Shaka
* @deprecated Use {@link PasswordProtectedNormalizer}
*/
public final class BouncyCastleSecurityProvider implements ISecurityProvider {
/**
*
*/
private static final String ALGORITHM_NAME = "AES/CBC/PKCS5Padding"; //$NON-NLS-1$
/**
*
*/
private static final String KEY_DERIVATION_ALGORITHM_NAME = "PKCS12"; //$NON-NLS-1$
/**
* The ramdomizer
*/
private static Random random = null;
public BouncyCastleSecurityProvider() {
}
private boolean needChecksum(IEncryptionData encData) {
IFileEntry entry = encData.getFileEntry();
if (entry != null) {
String path = entry.getPath();
return needChecksum(path);
}
return false;
}
/**
* @param entryPath
* @return
*/
private boolean needChecksum(String entryPath) {
return ArchiveConstants.CONTENT_XML.equals(entryPath) || ArchiveConstants.META_XML.equals(entryPath)
|| ArchiveConstants.STYLES_XML.equals(entryPath)
|| ArchiveConstants.PATH_MARKER_SHEET.equals(entryPath);
}
/*
* (non-Javadoc)
*
* @see
* org.xmind.core.internal.security.ISecurityProvider#initEncryptionData
* (org.xmind.core.IEncryptionData)
*/
public void initializeEncryptionData(IEncryptionData encData) {
if (needChecksum(encData)) {
encData.setChecksumType("MD5"); //$NON-NLS-1$
}
encData.setAttribute(ALGORITHM_NAME, TAG_ALGORITHM, ATTR_ALGORITHM_NAME);
encData.setAttribute(KEY_DERIVATION_ALGORITHM_NAME, TAG_KEY_DERIVATION, ATTR_KEY_DERIVATION_NAME);
encData.setAttribute(generateSalt(), TAG_KEY_DERIVATION, ATTR_SALT);
encData.setAttribute("1024", TAG_KEY_DERIVATION, ATTR_ITERATION_COUNT); //$NON-NLS-1$
}
private void checkEncryptionData(IEncryptionData encData) throws CoreException {
String algoName = encData.getAttribute(TAG_ALGORITHM, ATTR_ALGORITHM_NAME);
if (algoName == null || !ALGORITHM_NAME.equals(algoName))
throw new CoreException(Core.ERROR_FAIL_INIT_CRYPTOGRAM);
String keyAlgoName = encData.getAttribute(TAG_KEY_DERIVATION, ATTR_KEY_DERIVATION_NAME);
if (keyAlgoName == null || !KEY_DERIVATION_ALGORITHM_NAME.equals(keyAlgoName))
throw new CoreException(Core.ERROR_FAIL_INIT_CRYPTOGRAM);
}
private int getIterationCount(IEncryptionData encData) {
return encData.getIntAttribute(1024, TAG_KEY_DERIVATION, ATTR_ITERATION_COUNT);
}
private byte[] getSalt(IEncryptionData encData) throws CoreException {
String saltString = encData.getAttribute(TAG_KEY_DERIVATION, ATTR_SALT);
if (saltString == null)
throw new CoreException(Core.ERROR_FAIL_INIT_CRYPTOGRAM);
return Base64.base64ToByteArray(saltString);
}
public OutputStream createPasswordProtectedOutputStream(OutputStream output, boolean encrypt,
IEncryptionData encData, String password) throws CoreException {
BufferedBlockCipher cipher = createCipher(encrypt, encData, password);
OutputStream out = new BlockCipherOutputStream(output, cipher);
if (encData.getChecksumType() != null)
out = new ChecksumOutputStream(out);
return out;
}
public InputStream createPasswordProtectedInputStream(InputStream input, boolean encrypt, IEncryptionData encData,
String password) throws CoreException {
BufferedBlockCipher cipher = createCipher(encrypt, encData, password);
InputStream in = new BlockCipherInputStream(input, cipher);
if (encData.getChecksumType() != null)
in = new ChecksumInputStream(in);
return in;
}
private BufferedBlockCipher createCipher(boolean encrypt, IEncryptionData encData, String password)
throws CoreException {
checkEncryptionData(encData);
// Create a parameter generator
PKCS12ParametersGenerator paramGen = new PKCS12ParametersGenerator(new MD5Digest());
// Get the password bytes
byte[] pwBytes = password == null ? new byte[0]
: PBEParametersGenerator.PKCS12PasswordToBytes(password.toCharArray());
// Initialize the parameter generator with password bytes,
// salt and iteration counts
paramGen.init(pwBytes, getSalt(encData), getIterationCount(encData));
// Generate a parameter
CipherParameters param = paramGen.generateDerivedParameters(128);
// Create a block cipher
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
// Initialize the block cipher
cipher.init(encrypt, param);
return cipher;
}
private static Random getRandom() {
if (random == null)
random = new Random();
return random;
}
private static String generateSalt() {
return Base64.byteArrayToBase64(generateSaltBytes());
}
private static byte[] generateSaltBytes() {
byte[] bytes = new byte[8];
getRandom().nextBytes(bytes);
return bytes;
}
}