All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sendsafely.utils.CryptoUtil Maven / Gradle / Ivy

package com.sendsafely.utils;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Iterator;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPBEEncryptedData;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPBEDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.bc.BcPBEKeyEncryptionMethodGenerator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.encoders.UrlBase64;

import com.sendsafely.exceptions.MessageException;
import com.sendsafely.exceptions.PublicKeyDecryptionFailedException;
import com.sendsafely.exceptions.PublicKeyEncryptionFailedException;
import com.sendsafely.exceptions.SignatureCreationFailedException;
import com.sendsafely.exceptions.TokenGenerationFailedException;

public class CryptoUtil 
{

	private static final int RsaKeySize = 2048;
	private static final KeyFingerPrintCalculator KEY_FINGERPRINT_CALCULATOR = new BcKeyFingerprintCalculator();

	public static String createChecksum(String keyCode, String packageCode)
	{
		return PBKDF2(keyCode, packageCode, 1024);
	}
	
	public static String Sign(String key, String dataToSign) throws SignatureCreationFailedException
	{
		SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA256");

		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA256");
			mac.init(signingKey);
		} catch (InvalidKeyException e) {
			throw new SignatureCreationFailedException(e);
		} catch (NoSuchAlgorithmException e) {
			throw new SignatureCreationFailedException(e);
		}

		byte[] rawHmac = mac.doFinal(dataToSign.getBytes());
		
		// Hex encode and return
		
		return Hex.toHexString(rawHmac);
	}
	
	public static String GenerateKeyCode() throws TokenGenerationFailedException
	{
		byte[] randomBytes = new byte[32];
		
		SecureRandom sr;
		try {
			sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
			sr.nextBytes(randomBytes);
		} catch (NoSuchAlgorithmException e) {
			throw new TokenGenerationFailedException(e);
		} catch (NoSuchProviderException e) {
			throw new TokenGenerationFailedException(e);
		}
		return new String(UrlBase64.encode(randomBytes)).replace(".", "");
	}
	
	public static Keypair GenerateKeyPair() throws NoSuchAlgorithmException, PGPException, IOException{
		
		RSAKeyPairGenerator generator = new RSAKeyPairGenerator();
		RSAKeyGenerationParameters params = new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001), new SecureRandom(), RsaKeySize, 12);
		generator.init(params);
		AsymmetricCipherKeyPair keyPair = generator.generateKeyPair();
		Keypair pair = Armor(keyPair, "[email protected]");
		return pair;
	}
	
	private static Keypair Armor(AsymmetricCipherKeyPair keyPair, String email) throws PGPException, IOException, NoSuchAlgorithmException {
		PGPKeyPair pgpKeyPair = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL, keyPair, new Date());
		ByteArrayOutputStream memOut = new ByteArrayOutputStream(); // this is where we put the signed data
		ArmoredOutputStream secretOut = new ArmoredOutputStream(memOut);
		PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(PGPEncryptedData.AES_256);
		BcPGPContentSignerBuilder builder = new BcPGPContentSignerBuilder(pgpKeyPair.getPublicKey().getAlgorithm(), PGPEncryptedData.AES_256);
		PGPSecretKey secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, pgpKeyPair, email, sha1Calc, null, null, builder, null);
		secretKey.encode(secretOut);
		secretOut.close();
		ByteArrayOutputStream memPublicOut = new ByteArrayOutputStream();
		ArmoredOutputStream publicOut = new ArmoredOutputStream(memPublicOut);
		PGPPublicKey key = secretKey.getPublicKey();
		key.encode((OutputStream) publicOut);
		publicOut.close();
		String privateKeyStr = memOut.toString();
		String publicKeyStr = memPublicOut.toString();
		Keypair pair = new Keypair();
		pair.setPrivateKey(privateKeyStr);
		pair.setPublicKey(publicKeyStr);
		return pair;
	}

	public static String PBKDF2(String token, String salt, int iterations)
	{
		PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
		
	    generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(token.toCharArray()), salt.getBytes(), iterations);
	    KeyParameter params = (KeyParameter)generator.generateDerivedParameters(256);
	
		//return DatatypeConverter.printHexBinary(params.getKey().clone());	
	    return new String(Hex.encode(params.getKey().clone()));
	}
	
	public static void encryptFile(OutputStream out, InputStream inputStream,
			char[] passPhrase, String filename, long filesize) throws IOException, PGPException 
	{		
		OutputStream cOut = null;
		
		try 
		{
			BcPGPDataEncryptorBuilder pdeb = new BcPGPDataEncryptorBuilder(PGPEncryptedDataGenerator.AES_256);
			pdeb.setWithIntegrityPacket(true);
			PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(pdeb);
			PGPKeyEncryptionMethodGenerator method = new BcPBEKeyEncryptionMethodGenerator(passPhrase);
			cPk.addMethod(method);
			
			cOut = cPk.open(out, new byte[1 << 16]);

			writeFileToLiteralData(cOut, PGPLiteralData.BINARY, inputStream, new byte[1 << 16], filename, filesize);
		}
		finally
		{
			if(cOut != null)
				cOut.close();
		}
	}
	
	public String encrypt(String message, String encryptionKey) throws MessageException
	{
		byte[] messageBytes = message.getBytes(Charset.forName("UTF-8"));
		
		String encrypted = "";
        
        try
        {
        	BcPGPDataEncryptorBuilder pdeb = new BcPGPDataEncryptorBuilder(PGPEncryptedDataGenerator.AES_256);
        	pdeb.setWithIntegrityPacket(true);
	        PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(pdeb);
        	
	        ByteArrayOutputStream encOut = new ByteArrayOutputStream();
	        OutputStream out = encOut;
	        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
	        PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
	
	        OutputStream pOut = lData.open(bOut, // the compressed output stream
	                PGPLiteralData.BINARY, PGPLiteralData.CONSOLE,
	                messageBytes.length, // length of clear data
	                new Date() // current time
	                );
	        pOut.write(messageBytes);
	
	        lData.close();
	        bOut.close();
	
	        PGPKeyEncryptionMethodGenerator method = new BcPBEKeyEncryptionMethodGenerator(encryptionKey.toCharArray());
	        cPk.addMethod(method);
	        
	        byte[] bytes = bOut.toByteArray();
	
	        OutputStream cOut = cPk.open(out, bytes.length);
	
	        cOut.write(bytes); // obtain the actual bytes from the compressed stream
	
	        cOut.close();
	        
	        bOut.close();
	        
	        out.close();
        
	        byte[] encodedValue = Base64.encode(encOut.toByteArray());
	        encrypted = new String(encodedValue);
        }
        catch(Exception e)
        {
        	throw new MessageException(e);
        }
        
        return encrypted;
	}

	public static String decryptKeycode(String privateKey, String encryptedKeycode) throws IOException, NoSuchProviderException, PublicKeyDecryptionFailedException {
		InputStream keyIn = new BufferedInputStream(new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)));
		InputStream in = new BufferedInputStream(new ByteArrayInputStream(encryptedKeycode.getBytes(StandardCharsets.UTF_8)));

		in = PGPUtil.getDecoderStream(in);

		String keycode;
        
		try
		{
			PGPObjectFactory pgpF = new PGPObjectFactory(in, KEY_FINGERPRINT_CALCULATOR);
			PGPEncryptedDataList enc;
			
			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();
			}

			// find the secret key
			Iterator it = enc.getEncryptedDataObjects();
			PGPPrivateKey sKey = null;
			PGPPublicKeyEncryptedData pbe = null;
			PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn), KEY_FINGERPRINT_CALCULATOR);

			while (sKey == null && it.hasNext())
			{
				pbe = (PGPPublicKeyEncryptedData)it.next();
				sKey = findSecretKey(pgpSec, pbe.getKeyID());
			}

			if (sKey == null)
			{
				throw new IllegalArgumentException("secret key for message not found.");
			}

			InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));
			PGPObjectFactory plainFact = new PGPObjectFactory(clear, KEY_FINGERPRINT_CALCULATOR);
			Object message = plainFact.nextObject();

			if (message instanceof PGPLiteralData)
			{
				PGPLiteralData ld = (PGPLiteralData)message;
				
				InputStream unc = ld.getInputStream();
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				
				byte[] buff = new byte[1024];
				int ch;
				while ((ch = unc.read(buff)) >= 0){
					out.write(buff, 0, ch);;
				}
				
				keycode = out.toString();
				out.close();
			}
			else if (message instanceof PGPOnePassSignatureList)
			{
				throw new PGPException("encrypted message contains a signed message - not literal data.");
			}
			else
			{
				throw new PGPException("message is not a simple encrypted file - type unknown.");
			}
		}
		catch (PGPException e)
		{
			throw new PublicKeyDecryptionFailedException(e);
		}

		return keycode;
    }

    public static String encryptKeycode(String publicKeyStr, String keycode) throws PublicKeyEncryptionFailedException {

        byte[] messageBytes = keycode.getBytes(Charset.forName("UTF-8"));

        String encrypted;

        try {
            InputStream in=new ByteArrayInputStream(publicKeyStr.getBytes());
            in = PGPUtil.getDecoderStream(in);
            PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in, KEY_FINGERPRINT_CALCULATOR);
            PGPPublicKey key = null;
            Iterator rIt = pgpPub.getKeyRings();
            while (key == null && rIt.hasNext()) {
                PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
                Iterator kIt = kRing.getPublicKeys();
                while (key == null && kIt.hasNext()) {
                    PGPPublicKey k = (PGPPublicKey) kIt.next();
                    if (k.isEncryptionKey()) {
                        key = k;
                    }
                }
            }
            if (key == null) {
                throw new IllegalArgumentException("Can't find encryption key in key ring.");
            }

            // create an encrypted payload and set the public key on the data generator
            BcPGPDataEncryptorBuilder bcpgpdeb = new BcPGPDataEncryptorBuilder(PGPEncryptedData.AES_256);
			bcpgpdeb.setWithIntegrityPacket(true);
            PGPEncryptedDataGenerator encryptGen = new PGPEncryptedDataGenerator(bcpgpdeb);
            encryptGen.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(key));

            ByteArrayOutputStream encOut = new ByteArrayOutputStream();
            ArmoredOutputStream armoredOut = new ArmoredOutputStream(encOut);
            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
            PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();

            OutputStream pOut = lData.open(bOut, // the compressed output stream
                    PGPLiteralData.BINARY, PGPLiteralData.CONSOLE,
                    messageBytes.length, // length of clear data
                    new Date() // current time
            );
            pOut.write(messageBytes);

            lData.close();
            bOut.close();

            byte[] bytes = bOut.toByteArray();

            OutputStream cOut = encryptGen.open(armoredOut, bytes.length);

            cOut.write(bytes); // obtain the actual bytes from the compressed stream

            cOut.close();

            bOut.close();

            armoredOut.close();

            //byte[] encodedValue = Base64.encode(encOut.toByteArray());
            //encrypted = new String(encodedValue);
            encrypted = new String(encOut.toByteArray());
        } catch(Exception e) {
            throw new PublicKeyEncryptionFailedException(e);
        }

        return encrypted;
    }
	
	public static void decryptFile(InputStream input, OutputStream output, String decryptionKey, Progress progress) throws IOException, PGPException
	{
		input = PGPUtil.getDecoderStream(input);
		PGPObjectFactory pgpF = new PGPObjectFactory(input, KEY_FINGERPRINT_CALCULATOR);
		PGPEncryptedDataList enc;
		Object o = pgpF.nextObject();
		
		if (o instanceof  PGPEncryptedDataList) 
		{
			enc = (PGPEncryptedDataList) o;
		} 
		else 
		{
			enc = (PGPEncryptedDataList) pgpF.nextObject();
		}
		
		PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0);
		BcPGPDigestCalculatorProvider bcPgp = new BcPGPDigestCalculatorProvider();
		PBEDataDecryptorFactory pdf = new BcPBEDataDecryptorFactory(decryptionKey.toCharArray(), bcPgp);
		
		InputStream clear = pbe.getDataStream(pdf); 
		PGPObjectFactory pgpFact = new PGPObjectFactory(clear, KEY_FINGERPRINT_CALCULATOR);
		
		o = pgpFact.nextObject();
		
		if (o instanceof  PGPCompressedData) 
		{
			PGPCompressedData cData = (PGPCompressedData) o;
			pgpFact = new PGPObjectFactory(cData.getDataStream(), KEY_FINGERPRINT_CALCULATOR);
			o = pgpFact.nextObject();
		}

		PGPLiteralData ld = (PGPLiteralData) o;
		
		// This will decode for us.
		InputStream unc = ld.getInputStream();
		
		// Save content in file.
		byte[] buffer = new byte[1024];
		int len = unc.read(buffer);
		while (len != -1) {
			output.write(buffer, 0, len);
		    len = unc.read(buffer);
		    progress.updateCurrent(len);
		}
		
		// Finally verify the integrity
		if (pbe.isIntegrityProtected()) 
		{
			if (!pbe.verify()) 
			{
				throw new PGPException("Failed to verify the integrity check");
			} 
		} 
		else 
		{
			throw new PGPException("Package did not include integrity check");
		}
	}
	
	public static String decryptMessage(String message, String decryptionKey) throws MessageException {
		String decryptedString;
        
        byte[] messageBytes = message.getBytes(Charset.forName("UTF-8"));
		byte[] encrypted = Base64.decode(messageBytes);
		
        try
        {
            InputStream in = new ByteArrayInputStream(encrypted);
            in = PGPUtil.getDecoderStream(in);
		
            PGPObjectFactory pgpF = new PGPObjectFactory(in, KEY_FINGERPRINT_CALCULATOR);
            PGPEncryptedDataList enc = null;
            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();
            }
		   
            PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0);
            BcPGPDigestCalculatorProvider bcPgp = new BcPGPDigestCalculatorProvider();
            PBEDataDecryptorFactory pdf = new BcPBEDataDecryptorFactory(decryptionKey.toCharArray(), bcPgp);
            InputStream clear = pbe.getDataStream(pdf);

            PGPObjectFactory pgpFact = new PGPObjectFactory(clear, KEY_FINGERPRINT_CALCULATOR);
			
            PGPLiteralData  ld = (PGPLiteralData)pgpFact.nextObject();
			
            InputStream unc = ld.getInputStream();
			
            ByteArrayOutputStream out = new ByteArrayOutputStream();
			
            byte[] buff = new byte[1024];
            int ch;
            while ((ch = unc.read(buff)) >= 0){
                out.write(buff, 0, ch);;
            }
			
			decryptedString = out.toString();
			out.close();

			// Finally verify the integrity
			if (pbe.isIntegrityProtected())
			{
				if (!pbe.verify())
				{
					throw new MessageException();
				}
			}
        }
		catch(Exception e)
		{
			throw new MessageException(e.getMessage());
		}
		
		return decryptedString;	
	}

    private static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID)
            throws PGPException, NoSuchProviderException
    {
        PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);

        if (pgpSecKey == null)
        {
            return null;
        }

        return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().build(null));
    }

	private static void writeFileToLiteralData(OutputStream out, char fileType,
			InputStream inputStream, byte[] buffer, String filename, long filesize) throws IOException 
	{
		PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
		OutputStream pOut = lData.open(out, fileType, filename, filesize, new Date());
		
		byte[] buf = new byte[buffer.length];
		
		int len;
		int remainingBytesToRead = (int)filesize;
		int bytesToRead = Math.min(buf.length, remainingBytesToRead);
		while (remainingBytesToRead > 0 && (len = inputStream.read(buf, 0, bytesToRead)) > 0)
		{
			pOut.write(buf, 0, len);
			remainingBytesToRead -= len;
			bytesToRead = Math.min(buf.length, remainingBytesToRead);
		}
		
		inputStream.close();
		pOut.close();
	}

	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy