package cloudsync.helper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import cloudsync.exceptions.FileIOException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPBEEncryptedData;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.util.io.Streams;
import cloudsync.exceptions.CloudsyncException;
import cloudsync.model.Item;
import cloudsync.model.LocalStreamData;
import cloudsync.model.TempInputStream;
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPBEDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.bc.BcPBEKeyEncryptionMethodGenerator;
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
public class Crypt
{
// private final static Logger LOGGER =
// Logger.getLogger(Crypt.class.getName());
private final static Logger LOGGER = Logger.getLogger(Crypt.class.getName());
private final static int BUFFER_SIZE = 1 << 16;
private final static int ENCRYPT_ALGORITHM = PGPEncryptedDataGenerator.AES_256;
private final static boolean ENCRYPT_ARMOR = false;
private final String passphrase;
private final boolean showProgress;
private final long minTmpFileSize;
private final boolean useJCE;
public Crypt(final CmdOptions options)
{
passphrase = options.getPassphrase();
showProgress = options.showProgress();
minTmpFileSize = options.getMinTmpFileSise();
int allowedKeyLength = 0;
try
{
allowedKeyLength = Cipher.getMaxAllowedKeyLength("AES");
}
catch (final NoSuchAlgorithmException e)
{
// e.printStackTrace();
}
if (allowedKeyLength < Integer.MAX_VALUE)
{
useJCE = false;
LOGGER.warning("Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files are not installed");
}
else
{
useJCE = true;
}
Security.addProvider(new BouncyCastleProvider());
}
public String decryptText(String text) throws CloudsyncException
{
text = text.replace('_', '/');
final byte[] data = Base64.decodeBase64(text);
try
{
return new String(Streams.readAll(decryptData(new ByteArrayInputStream(data))));
}
catch (IOException e)
{
throw new CloudsyncException("can't encrypt data", e);
}
}
public InputStream decryptData(final InputStream stream) throws CloudsyncException
{
InputStream in;
try
{
in = PGPUtil.getDecoderStream(stream);
final PGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
PGPEncryptedDataList enc;
final Object o = pgpF.nextObject();
// the first object might be a PGP marker packet.
if (o instanceof PGPEncryptedDataList)
{
enc = (PGPEncryptedDataList) o;
}
else
{
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
final PGPPBEEncryptedData pbe = (PGPPBEEncryptedData) enc.get(0);
PBEDataDecryptorFactory pbeDataDecryptorFactory;
if(useJCE)
{
pbeDataDecryptorFactory = new JcePBEDataDecryptorFactoryBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("BC").build()).setProvider("BC").build(passphrase.toCharArray());
}
else
{
BcPGPDigestCalculatorProvider pgpDigestCalculatorProvider = new BcPGPDigestCalculatorProvider();
pbeDataDecryptorFactory = new BcPBEDataDecryptorFactory(passphrase.toCharArray(), pgpDigestCalculatorProvider);
}
final InputStream clear = pbe.getDataStream(pbeDataDecryptorFactory);
PGPObjectFactory pgpFact = new JcaPGPObjectFactory(clear);
final PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject();
pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
final PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
return ld.getInputStream();
}
catch (Exception e)
{
throw new CloudsyncException("can't decrypt data", e);
}
}
public LocalStreamData encryptedBinary(final String name, final LocalStreamData data, final Item item) throws FileIOException
{
InputStream input = null;
try
{
input = data.getStream();
if (data.getLength() < minTmpFileSize)
{
final ByteArrayOutputStream output = new ByteArrayOutputStream();
_encryptData(output, input, data.getLength(), name, item.getInfo(), ENCRYPT_ALGORITHM, ENCRYPT_ARMOR);
final byte[] bytes = output.toByteArray();
return new LocalStreamData(new ByteArrayInputStream(bytes), bytes.length);
}
else
{
try
{
File temp = File.createTempFile("encrypted", ".pgp");
temp.deleteOnExit();
final FileOutputStream output = new FileOutputStream(temp);
_encryptData(output, input, data.getLength(), name, item.getInfo(), ENCRYPT_ALGORITHM, ENCRYPT_ARMOR);
return new LocalStreamData(new TempInputStream(temp), temp.length());
}
catch (IOException e)
{
throw new FileIOException("can't encrypt data", e);
}
}
}
finally
{
if (input != null) IOUtils.closeQuietly(input);
}
}
public String encryptText(String text) throws FileIOException
{
final ByteArrayOutputStream output = new ByteArrayOutputStream();
final byte[] bytes = text.getBytes();
_encryptData(output, new ByteArrayInputStream(bytes), bytes.length, PGPLiteralData.CONSOLE, null, ENCRYPT_ALGORITHM, ENCRYPT_ARMOR);
text = Base64.encodeBase64String(output.toByteArray());
text = text.replace('/', '_');
return text;
}
private void _encryptData(final OutputStream output, final InputStream input, final long length, final String name, final String fileOutputInfo, final int algorithm, final boolean armor)
throws FileIOException
{
OutputStream out = output;
try
{
if (armor) out = new ArmoredOutputStream(out);
PGPEncryptedDataGenerator encryptedDataGenerator;
if(useJCE)
{
encryptedDataGenerator = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(algorithm).setSecureRandom(
new SecureRandom()).setProvider("BC"));
encryptedDataGenerator.addMethod(new JcePBEKeyEncryptionMethodGenerator(passphrase.toCharArray()).setProvider("BC"));
}
else
{
PGPDataEncryptorBuilder pgpDataEncryptorBuilder = new BcPGPDataEncryptorBuilder(algorithm);
encryptedDataGenerator = new PGPEncryptedDataGenerator(pgpDataEncryptorBuilder);
encryptedDataGenerator.addMethod(new BcPBEKeyEncryptionMethodGenerator(passphrase.toCharArray()));
}
final OutputStream encryptedData = encryptedDataGenerator.open(out, new byte[BUFFER_SIZE]);
PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
OutputStream compressedOut = compressedDataGenerator.open(encryptedData);
final PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
final OutputStream literalOut = literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY, name, new Date(), new byte[BUFFER_SIZE]);
byte[] buffer = new byte[BUFFER_SIZE];
int len;
if (showProgress && output instanceof FileOutputStream && fileOutputInfo != null )
{
double current = 0;
DecimalFormat df = new DecimalFormat("00");
while ((len = input.read(buffer)) != -1)
{
literalOut.write(buffer, 0, len);
current += len;
String msg = "\r " + df.format(Math.ceil(current * 100 / length)) + "% (" + convertToKB(current) + " of " + convertToKB(length)
+ " kb) encrypted of " + fileOutputInfo;
LOGGER.log(Level.FINEST, msg, true);
}
}
else
{
while ((len = input.read(buffer)) != -1)
{
literalOut.write(buffer, 0, len);
}
}
input.close();
literalOut.close();
literalDataGenerator.close();
compressedOut.close();
compressedDataGenerator.close();
encryptedData.close();
encryptedDataGenerator.close();
}
catch (Exception e)
{
throw new FileIOException("can't encrypt data", e);
}
finally
{
if (armor)
{
try
{
out.close();
}
catch (IOException e)
{
throw new FileIOException("can't close armor", e);
}
}
try
{
output.close();
}
catch (IOException e)
{
throw new FileIOException("can't close output", e);
}
}
}
private long convertToKB(double size)
{
return (long) Math.ceil(size / 1024);
}
}