How to encrypt/decrypt text files using ElGamal

celax picture celax · Feb 4, 2011 · Viewed 11k times · Source

I'm trying to encrypt and decrypt text files using ElGamal for my study but it seems that I could not make it work correctly. I have a group of text files ranging from 1kb - 1mb, and I'm using 512bit for my key size. I already know that just like RSA, ELGamal can't encrypt values more than its modulus so as my initial solution, I've decided to divide each file into chunks(which is smaller than its modulus) for me to be able to encrypt it and luckily these solution works for encryption. My problem is this,when I tried to decrypt it, outputs that has been generated is not the actual output I'm expecting to see. I don't know what's the cause of my problem and I really need to find a solution within few days.

I'll be showing you some of my code snippets just to make it clear.

I had generated my keypair with the following

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ElGamal", "BC";
keyGen.initialize(512);

I encrypt by calling

public static void encryptFile(String srcFileName, String destFileName, PublicKey key) throws Exception
{
    encryptDecryptFile(srcFileName,destFileName, key, Cipher.ENCRYPT_MODE);
}

and I decrypt by calling

public static void decryptFile(String srcFileName, String destFileName, PrivateKey key) throws Exception
{
    encryptDecryptFile(srcFileName,destFileName, key, Cipher.DECRYPT_MODE);
}

Here's the definition of encryptDecryptFile(..) method

public static void encryptDecryptFile(String srcFileName, String destFileName, Key key, int cipherMode) throws Exception
    {
        OutputStream outputWriter = null;
        InputStream inputReader = null;
        try
        {
            Cipher cipher = Cipher.getInstance("ElGamal/None/NoPadding", "BC"");
            String textLine = null;
    //buffer(my chunks) depends wether it is encyption or decryption
            byte[] buf = (cipherMode == Cipher.ENCRYPT_MODE? new byte[50] : new byte[64]);
            int bufl;
            // init the Cipher object for Encryption...
            cipher.init(cipherMode, key);

            // start FileIO
            outputWriter = new FileOutputStream(destFileName);
            inputReader = new FileInputStream(srcFileName);
            while ( (bufl = inputReader.read(buf)) != -1)
            {
                byte[] encText = null;
                if (cipherMode == Cipher.ENCRYPT_MODE)
                {
                      encText = encrypt(copyBytes(buf,bufl),(PublicKey)key);
                }
                else
                {
                    if (_log.isDebugEnabled())
                    {
                        System.out.println("buf = " + new String(buf));
                    }
                    encText = decrypt(copyBytes(buf,bufl),(PrivateKey)key);
                }
                outputWriter.write(encText);
                if (_log.isDebugEnabled())
                {
                    System.out.println("encText = " + new String(encText));
                }
            }
            outputWriter.flush();

        }
        catch (Exception e)
        {
            _log.error(e,e);
            throw e;
        }
        finally
        {
            try
            {
                if (outputWriter != null)
                {
                    outputWriter.close();
                }
                if (inputReader != null)
                {
                    inputReader.close();
                }
            }
            catch (Exception e)
            {
                // do nothing...
            } // end of inner try, catch (Exception)...
        }
    }

For copyBytes:

public static byte[] copyBytes(byte[] arr, int length)
{
    byte[] newArr = null;
    if (arr.length == length)
    {
        newArr = arr;
    }
    else
    {
        newArr = new byte[length];
        for (int i = 0; i < length; i++)
        {
            newArr[i] = (byte) arr[i];
        }
    }
    return newArr;
}

For encypt(...)

    public static byte[] encrypt(byte[] text, PublicKey key) throws Exception
{
    byte[] cipherText = null;
    try
    {

        Cipher cipher = Cipher.getInstance("ElGamal/None/NoPadding", "BC"");
        if (_log.isDebugEnabled())
        {
            _log.debug("\nProvider is: " + cipher.getProvider().getInfo());
            _log.debug("\nStart encryption with public key");
        }

        // encrypt the plaintext using the public key
        cipher.init(Cipher.ENCRYPT_MODE, key);
        cipherText = cipher.doFinal(text);
    }
    catch (Exception e)
    {
        _log.error(e, e);
        throw e;
    }
    return cipherText;
}

and decrypt(..)

   public static byte[] decrypt(byte[] text, PrivateKey key) throws Exception
    {
        byte[] dectyptedText = null;
        try
        {
            // decrypt the text using the private key
            Cipher cipher = Cipher.getInstance("ElGamal/None/NoPadding", "BC"");
              cipher.init(Cipher.DECRYPT_MODE, key);
            dectyptedText = cipher.doFinal(text);
        }
        catch (Exception e)
        {
            _log.error(e, e);
            throw e;
        }
        return dectyptedText;

    }

Original code by Aviran Mondo

I thinks that's all what you need, just tell me if you want to see the full source code. Thanks,

Answer

templatetypedef picture templatetypedef · Feb 4, 2011

This isn't quite related to your code, but it is not cryptographically secure to try to turn a block cipher with a fixed-width block size into a block cipher that can work on a stream by just splitting the input up into blocks and encrypting each of them. If you do this, you essentially are doing a glorified monoalphabetic substitution cipher where each "character" is one block wide. This allows an attacker to recover parts of the structure of your input, which ruins the guarantee you normally get from these cryptographic primitives. As an example, see this Wikipedia discussion of this particular mode of encryption and see how it encrypts Tux the Linux Penguin. The encrypted image immediately allows you to see the structure of the input.

If you want to use a block cipher like ElGamal to encrypt a stream of text, you should use a more complex construction like cipher block chaining (CBC) or counter mode (CTR), which are provably cryptographically secure over inputs of reasonable size. An attacker would have a an extremely hard time trying to break your security if you used one of these modes.

I apologize for not having anything more substantial to say about your code, but I honestly think that it's worth backing up and picking a strong crypto system before trying to debug this one. Otherwise you'll end up with a system that a clever attacker could foil.