0

I am trying to encrypt and decrypt the image file with my Helper class RSAHelper.java

package com.lib;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;

/**
 * Created by shobhan.
 */
public class RSAHelper {
    private final static String RSA = "RSA";

    private PublicKey publicKey;
    private PrivateKey privateKey;

    public RSAHelper() throws Exception {
        /**
         * generate RSA keys
         */
        generateKey();
    }

    private void generateKey() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        publicKey = keyPair.getPublic();
        privateKey = keyPair.getPrivate();
    }

    /**
     * Used to do encryptFile the file
     *
     * @param srcPath  File path to be encrypted
     * @param destPath Encrypts the file in srcPath and creates a file in destPath
     * @return true if encryption success, false otherwise
     */
    public boolean encryptFile(String srcPath, String destPath) {

        try {
            FileInputStream fileInputStream = new FileInputStream(srcPath);
            FileOutputStream fileOutputStream = new FileOutputStream(destPath);

            // byte[] key = "12345678".getBytes();
            // SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);

            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.PUBLIC_KEY, publicKey);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // cipher.init(Cipher.ENCRYPT_MODE, keySpec);

            // CipherOutputStream cipherOutputStream = new
            // CipherOutputStream(fileOutputStream, cipher);

            byte[] buf = new byte[117];
            byte[] encryptedData = null;
            int read;
            while ((read = fileInputStream.read(buf)) > 0) {
                encryptedData = cipher.doFinal(buf);
                fileOutputStream.write(encryptedData);
                // cipherOutputStream.write(buf);
            }

            fileInputStream.close();
            fileOutputStream.flush();
            // cipherOutputStream.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Used to do decryptFile the file
     *
     * @param srcPath  File path to be decrypted
     * @param destPath Decrypts the file in srcPath and creates a file in destPath
     * @return true if encryption success, false otherwise
     */
    public boolean decryptFile(String srcPath, String destPath) {

        try {

            FileInputStream fileInputStream = new FileInputStream(srcPath);
            FileOutputStream fileOutputStream = new FileOutputStream(destPath);

            // byte[] key = "12345678".getBytes();
            // SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);

            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.PRIVATE_KEY, privateKey);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            // cipher.init(Cipher.DECRYPT_MODE, keySpec);

            // CipherOutputStream cipherOutputStream = new
            // CipherOutputStream(fileOutputStream, cipher);

            byte[] buf = new byte[128];
            byte[] decryptedData = null;
            int read;
            while ((read = fileInputStream.read(buf)) > 0) {
                decryptedData = cipher.doFinal(buf);
                fileOutputStream.write(decryptedData);
                // cipherOutputStream.write(buf);
            }

            fileInputStream.close();
            fileOutputStream.flush();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}

in Activity I used like

RSAHelper rsaHelper = null;
        try {
            rsaHelper = new RSAHelper();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String src = getExternalFilesDir("sdcard").getAbsolutePath() + "/mother.png";
        String dest = src.replace("mother","motherEnc");
        String decrypted = dest.replace("motherEnc","motherDec");

        rsaHelper.encryptFile(src, dest);
        rsaHelper.decryptFile(dest, decrypted);

but the decrypted file is not original(means corrupted).

Same code working when I execute in windows desktop.

Am I doing anything wrong?

Thanks in advance.

1
  • 1. RSA is not designed to encrypt large data, it is limited to a data size less than the key size. With a 1024 key size the data size is limited to less than 128-bytes. Data is generally encrypted with a symmetric algorithm such as AES. 2. Why are you using RSA public/private key encryption? If you really need a public/private key pair you need to use hybrid encryption where the data is encrypted with a symmetric key and that key is encrypted with asymmetric encryption such as RSA.
    – zaph
    Commented Oct 19, 2016 at 14:08

1 Answer 1

3

Continuing the list begun in the comment by @zaph:

  1. You are initializing the cipher twice, luckily with identical modes so it's just confusing but not the source of your bug.
  2. You are relying on defaults for the Cipher transformation, a frequent source of non-portability, bugs and/or confusion. Always explicitly specify the Cipher transformation fully, like Cipher.getInstance("RSA/ECB/PKCS1PADDING") or Cipher.getInstance("RSA/ECB/NOPADDING").
  3. You are incorrectly assuming that InputStream.read() will always return a full buffer of bytes if they are available. That might be how current implementations of FileInputStream actually work, but that is just luck. That behavior can change without warning at any time. InputStream implementors only need to obey the InputStream API contract, and so that is all you should rely on. You do record the number of bytes actually read, but then you don't do anything with it.
  4. You are flushing your OutputStream but not closing it. You should close it instead.

Your bug is caused by number 5. Since there is no guarantee that your input file is an exact multiple of 117 bytes, the last block will likely be less than 117 bytes. However, you then encrypt the full 117 byte block, of which the trailing bytes are simply what was leftover from the previous read. So your decrypted file will be a multiple of 117, and will match up to the original length, with the trailing bytes being identical to the trailing bytes of the previous block.

1
  • Thank you very much James. I understood the problem. but I can't get the solution. Can you please give me the best way of encryption and decryption . Suggest any library if available.
    – shobhan
    Commented Oct 20, 2016 at 5:27

Not the answer you're looking for? Browse other questions tagged or ask your own question.