Search code examples
javaandroidsecurityencryptionbouncycastle

Decrypting String using rsa key


I've encoded a simple json data using RSA public key and now I'm trying to decode it. The encoding part was done via terminal and the decoding is being performed programmatically. To verify the integrity of the encrypted file, I decrypted it via terminal and it works just fine. Now that I'm trying to decrypt the file programmatically, I'm running into decrypting issues. I can read the private_key.pem file perfectly and pass it to Cipher for decrypting the encoded file, however upon doing this I get the following exception.

java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block
 at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(CipherSpi.java:457)
 at javax.crypto.Cipher.doFinal(Cipher.java:1204)
 at com.benchmark.openssl.RSADecryption.decipherString(RSADecryption.java:295)
 at com.benchmark.openssl.RSADecryption.main(RSADecryption.java:263)
 at com.benchmark.MainActivity$1.onComplete(MainActivity.java:157)
 at io.reactivex.internal.operators.completable.CompletableObserveOn$ObserveOnCompletableObserver.run(CompletableObserveOn.java:90)
 at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:463)
 at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
 at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
 at java.util.concurrent.FutureTask.run(FutureTask.java:237)
 at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
 at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
 at java.lang.Thread.run(Thread.java:841)

OpenSSL Commands used:

openssl genrsa -out priv_key.pem 2048  
openssl rsa -pubout -in priv_key.pem -out pub_key.pem 
openssl rsautl -encrypt -in userdata.json -out user_encrypted_with_pub_key -inkey pub_key.pem –pubin
openssl rsautl -decrypt -in user_encrypted_with_pub_key -inkey priv_key.pem --> This is what I'm trying to do programmatically.

Code:

import org.spongycastle.util.io.pem.PemObject;
import org.spongycastle.util.io.pem.PemReader;
import android.util.Base64;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public static void main(String privateKeyPath, String encodedFilePath) throws FileNotFoundException,
            IOException, NoSuchAlgorithmException, NoSuchProviderException {
        Security.addProvider(new BouncyCastleProvider());

        String encodedString = readFileAsString(encodedStringPath);
        Timber.v("Encoded String: %s", encodedString);

        KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
        try {
            PrivateKey priv = generatePrivateKey(factory, privateKeyPath);
            Timber.i(String.format("Instantiated private key: %s", priv));
            decipherString(priv, encodedString);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
    }

    private static PrivateKey generatePrivateKey(KeyFactory factory,
                                                 String filename) throws InvalidKeySpecException,
            FileNotFoundException, IOException {
        PemFile pemFile = new PemFile(filename, false);
        byte[] content = pemFile.getPemObject().getContent();
        PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
        return factory.generatePrivate(privKeySpec);
    }

    private static void decipherString(PrivateKey privateKey, String encodedStringData) {
        byte[] dectyptedText = null;
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            dectyptedText = cipher.doFinal(encodedStringData.getBytes()); <---- EXCEPTION HERE
            Timber.w("Deciphered text is: %s", new String(dectyptedText));
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    static class PemFile {

        private PemObject pemObject;

        public PemFile(String filename, boolean isBase64) throws FileNotFoundException, IOException {
            PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream(filename)));
            try {
                this.pemObject = pemReader.readPemObject();
            } 
            catch (Exception e) {
                e.printStackTrace();
            }
            finally {
                pemReader.close();
            }
        }

        public PemObject getPemObject() {
            return pemObject;
        }
    }

userdata.json:

{
    "username":"umer",
    "password":"123456",
    "pin" : "123"
}

Solution

  • I figured it out by some trial and error since I don't have much knowledge about openssl. Anyways the process for decrypting an encrypted file should be the following.

    Terminal:

    String -> (Encrypt) -> Encrypted String -> (convert to base64) -> EncryptedBase64EncodedString -> (Decrypt) -> Original String
    

    Programmtically:

    EncryptedBase64EncodedString -> (convert from base64 to normal string [Use Default parameters only! No Padding or other constants for decoding base64 string]) -> Pass private_key & decoded string to Cipher -> Profit.
    

    The resultant code is:

    import org.spongycastle.util.io.pem.PemObject;
    import org.spongycastle.util.io.pem.PemReader;
    
    import android.util.Base64;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.StringReader;
    import java.security.GeneralSecurityException;
    import java.security.KeyFactory;
    import java.security.KeyPair;
    import java.security.NoSuchAlgorithmException;
    import java.security.NoSuchProviderException;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.Security;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.RSAPrivateCrtKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    
    import javax.crypto.Cipher;
    
    public static void main(String privateKeyPath, String publicKeyPath, String encodedStringPath, boolean isPublicKeyAndDataBase64) throws FileNotFoundException,
                IOException, NoSuchAlgorithmException, NoSuchProviderException {
            Security.addProvider(new BouncyCastleProvider());
    
            String encodedString = readFileAsString(encodedStringPath);
            if(isPublicKeyAndDataBase64) {
                KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
                Timber.w("Encoded String converted from base64: %s", decodeBase64ToBytesa(encodedString));
                try {
                    PrivateKey priv = generatePrivateKey(factory, privateKeyPath);
                    Timber.i(String.format("Instantiated private key: %s", priv));
                    decipherString(priv, decodeBase64ToBytesa(encodedString));
                } catch (InvalidKeySpecException e) {
                    e.printStackTrace();
                }
                return;
            }
            else
                Timber.w("Encoded String: %s", encodedString);
    
            KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
            try {
                PrivateKey priv = generatePrivateKey(factory, privateKeyPath);
                Timber.i(String.format("Instantiated private key: %s", priv));
                decipherString(priv, encodedString.getBytes());
                PublicKey pub = generatePublicKey(factory, publicKeyPath);
                Timber.i(String.format("Instantiated public key: %s", pub));
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            }
        }
    
        private static PrivateKey generatePrivateKey(KeyFactory factory,
                                                     String filename) throws InvalidKeySpecException,
                FileNotFoundException, IOException {
            PemFile pemFile = new PemFile(filename, false);
            byte[] content = pemFile.getPemObject().getContent();
            PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
            return factory.generatePrivate(privKeySpec);
        }
    
        private static PublicKey generatePublicKey(KeyFactory factory,
                                                   String filename) throws InvalidKeySpecException,
                FileNotFoundException, IOException {
            PemFile pemFile = new PemFile(filename, false);
            byte[] content = pemFile.getPemObject().getContent();
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
            return factory.generatePublic(pubKeySpec);
        }
    
        private static void decipherString(PrivateKey privateKey, byte[] encodedStringData) {
            byte[] dectyptedText = null;
            try {
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(Cipher.DECRYPT_MODE, privateKey);
                dectyptedText = cipher.doFinal(encodedStringData);
                Timber.w("Deciphered text is: %s", new String(dectyptedText));
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        static class PemFile {
    
            private PemObject pemObject;
    
            public PemFile(String filename, boolean isBase64) throws FileNotFoundException, IOException {
                PemReader pemReader = null;
    
                if(isBase64) {
                    Timber.i("reading base64 encoded pem file. base64DecodedString: %s", decodeBase64(filename));
                    pemReader = new PemReader(new StringReader(decodeBase64(filename)));
                }
                else
                    pemReader = new PemReader(new InputStreamReader(
                            new FileInputStream(filename)));
                try {
                    this.pemObject = pemReader.readPemObject();
                }
                catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    pemReader.close();
                }
            }
    
            public PemObject getPemObject() {
                return pemObject;
            }
        }