0

I have to implement logic to encrypt or decrypt very big files ( 10GB size ). File size is a big concern for us. Hence, it has to be done by transferring data from input stream to output stream, so that we do not have to be concerned with the amount of data.

I first implemented logic with org.bouncycastle api . that worked for few hundred MB files but failed with java heap space error when tried with 10GB file. I increased heap size upto 4 GB but that did not work. It happened as at somepoint, it holds the whole amount of data in memory( after compressing / decompressing )

In 2nd phase we moved to javax.crypto.cypher thinking that it is a better fit for our design and it is working with input/output stream . Hence file size would not matter. But ended up with error :

xception in thread "main" java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block

Below is a test program that worked for us when tested with very small sized file but failed when tried with big sized file. Please advise how to improve this program /what to do to make it work with huge file.

            package com.company.digtal.web.generic.utils;


            import java.io.File;
            import java.io.FileInputStream;
            import java.io.FileNotFoundException;
            import java.io.FileOutputStream;
            import java.io.IOException;
            import java.io.InputStream;
            import java.io.OutputStream;

            import java.security.InvalidKeyException;
            import java.security.Key;

            import java.security.NoSuchAlgorithmException;
            import java.security.NoSuchProviderException;
            import java.security.PrivateKey;
            import java.security.PublicKey;
            import java.security.Security;

            import java.util.Iterator;

            import javax.crypto.Cipher;
            import javax.crypto.CipherInputStream;
            import javax.crypto.CipherOutputStream;
            import javax.crypto.NoSuchPaddingException;

            import org.bouncycastle.jce.provider.BouncyCastleProvider;
            import org.bouncycastle.openpgp.PGPException;
            import org.bouncycastle.openpgp.PGPPrivateKey;
            import org.bouncycastle.openpgp.PGPPublicKey;
            import org.bouncycastle.openpgp.PGPPublicKeyRing;
            import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
            import org.bouncycastle.openpgp.PGPSecretKey;
            import org.bouncycastle.openpgp.PGPSecretKeyRing;
            import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
            import org.bouncycastle.openpgp.PGPUtil;
            import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;




            public  class CipherEncryptionUtil 
            {

                public static void main( 
                        String[] args) 
                        throws Exception 
                    { 
                        Security.addProvider(new BouncyCastleProvider()); 

                        String clearTxtInputFilePath = "C:\\temp\\delete\\PGP_Test\\big_input.txt";
                        String encryptedFilePath = "C:\\temp\\delete\\PGP_Test\\output\\encrypted_output_big.pgp";
                        String decryptedFilePath = "C:\\temp\\delete\\PGP_Test\\output\\decrypted_output_big.txt";

                        String publicKeyPath = "C:\\temp\\delete\\PGP_Test\\Online-eng-request_public.asc";

                        String privateKeyPath = "C:\\temp\\delete\\PGP_Test\\secring.skr";
                        String passPhrase = "Jabble wants to chase a lady bug";


                        //Encrypt plain txt file with public key
                        OutputStream    out = new FileOutputStream(encryptedFilePath); 

                        FileInputStream fis = new FileInputStream(clearTxtInputFilePath);
                        CipherOutputStream cos = encrypt(out, publicKeyPath); 


                        int i;
                        byte[] block = new byte[2048];
                        while ((i = fis.read(block)) != -1) {
                            cos.write(block, 0, i);
                             }


                        cos.close();
                        fis.close();



                        //Decrypt plain txt file with private key
                        CipherInputStream    cIn = decrypt(new FileInputStream(encryptedFilePath),privateKeyPath, passPhrase.toCharArray()); 
                        FileOutputStream fClearTxtOs = new FileOutputStream(decryptedFilePath);

                        while ((i = cIn.read(block)) != -1) {
                            fClearTxtOs.write(block, 0, i);
                             }
                        fClearTxtOs.close();
                        cIn.close();

                    } 

                public static CipherOutputStream encrypt(OutputStream outputStream, String publicKeyPath)
                {
                    Cipher cipher;
                    Key publicKey = null;

                    try 
                    {
                        cipher = Cipher.getInstance("RSA", "BC");

                    } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
                        String msg = "failed to create output stream";
                        System.out.println(  msg + " : " + e.getMessage());
                        e.printStackTrace();
                        throw new RuntimeException( msg, e );
                    }

                    try {
                        publicKey = getPublicKey(publicKeyPath);
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    try {
                        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
                    } catch (InvalidKeyException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    return ( new CipherOutputStream(outputStream, cipher));
                }


                public static CipherInputStream  decrypt(InputStream inputStream, 
                        String  keyIn, 
                        char[]      passwd)
                {
                    Cipher cipher;
                    Key secretKey = null;

                    try 
                    {
                        cipher = Cipher.getInstance("RSA", "BC");

                    } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
                        String msg = "failed to create output stream";
                        System.out.println(  msg + " : " + e.getMessage());
                        e.printStackTrace();
                        throw new RuntimeException( msg, e );
                    }

                    try {
                        secretKey = getPrivateKey(new FileInputStream(keyIn),passwd);
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    try {
                        cipher.init(Cipher.DECRYPT_MODE, secretKey);
                    } catch (InvalidKeyException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    return ( new CipherInputStream(inputStream, cipher));
                }







                public static PublicKey getPublicKey(
                    String filePath)
                    throws PGPException, NoSuchProviderException, FileNotFoundException, IOException
                {
                    PGPPublicKey    encKey = readPublicKey(new FileInputStream(filePath));
                    return new JcaPGPKeyConverter().setProvider("BC").getPublicKey(encKey);
                }




                public static PrivateKey getPrivateKey( 
                        InputStream    in, char[]      passwd) 
                        throws IOException, PGPException, NoSuchProviderException 
                    { 
                        in = PGPUtil.getDecoderStream(in); 

                        PGPSecretKeyRingCollection        pgpSec = new PGPSecretKeyRingCollection(in); 

                        // 
                        // we just loop through the collection till we find a key suitable for encryption, in the real 
                        // world you would probably want to be a bit smarter about this. 
                        // 

                        // 
                        // iterate through the key rings. 
                        // 
                        Iterator<?> rIt = pgpSec.getKeyRings(); 

                        while (rIt.hasNext()) 
                        { 
                            PGPSecretKeyRing    kRing = (PGPSecretKeyRing)rIt.next();     
                            Iterator<?>                        kIt = kRing.getSecretKeys(); 

                            while (kIt.hasNext()) 
                            { 
                                PGPSecretKey    k = (PGPSecretKey)kIt.next(); 

                                if (k != null) 
                                { 
                                    PGPPrivateKey pk = k.extractPrivateKey(passwd, "BC"); 
                                     return new JcaPGPKeyConverter().setProvider("BC").getPrivateKey(pk);
                                } 
                            } 
                        } 

                        throw new IllegalArgumentException("Can't find secured key in key ring."); 
                    } 

                public static PGPPublicKey readPublicKey( 
                        InputStream    in) 
                        throws IOException, PGPException 
                    { 
                        in = PGPUtil.getDecoderStream(in); 

                        PGPPublicKeyRingCollection        pgpPub = new PGPPublicKeyRingCollection(in); 

                        // 
                        // we just loop through the collection till we find a key suitable for encryption, in the real 
                        // world you would probably want to be a bit smarter about this. 
                        // 

                        // 
                        // iterate through the key rings. 
                        // 
                        Iterator<?> rIt = pgpPub.getKeyRings(); 

                        while (rIt.hasNext()) 
                        { 
                            PGPPublicKeyRing    kRing = (PGPPublicKeyRing)rIt.next();     
                            Iterator<?>                        kIt = kRing.getPublicKeys(); 

                            while (kIt.hasNext()) 
                            { 
                                PGPPublicKey    k = (PGPPublicKey)kIt.next(); 

                                if (k.isEncryptionKey()) 
                                { 
                                    return k; 
                                } 
                            } 
                        } 

                        throw new IllegalArgumentException("Can't find encryption key in key ring."); 
                    } 




            }

Our key is RSA 2048 bit

Key property:

enter image description here

VictorGram
  • 2,521
  • 7
  • 48
  • 82
  • 1
    Your resource management is appalling, this is 2016; use `try-with-resources`. – Boris the Spider Oct 07 '16 at 15:21
  • Never encrypt large data with RSA. Always use hybrid encryption: Generate AES key, encrypt key with RSA and save it. Then encrypt data with the generated AES key. You can decrypt files of any size using CipherInputStream (block by block). – Robert Oct 07 '16 at 15:22
  • Furthermore, you simply cannot encrypt 10GB with RSA - this is an _asymmetric_ algorithm so it can only encrypt (a little less than) its key size. It looks like the encrypted files have `pgp` in their extension - in that case you're going to have to use PGP to decrypt them. Sorry to be blunt, but do you actually know what you're doing? – Boris the Spider Oct 07 '16 at 15:23
  • 2
    @Robert it's not a case of "_[n]ever encrypt large data with RSA_". It's a case of RSA **cannot** encrypt large files. – Boris the Spider Oct 07 '16 at 15:23
  • @Boris the Spider: If you want you can, just create blocks smaller than key-size and use it as block cipher. Luckily common implementations do not implement it as it doe snot make much sense.... – Robert Oct 07 '16 at 15:25
  • 2
    @Robert that's about as secure as shouting your cleartext from a rooftop in a funny accept under the assumption that no-one can understand you. There is no block mode for RSA, and never has been. There is no way to encrypt large files with RSA. – Boris the Spider Oct 07 '16 at 15:26
  • See also http://stackoverflow.com/a/13501360/3474 – erickson Oct 07 '16 at 16:11

0 Answers0