Search code examples
javaencryptioncryptographyaesrsa

AES + RSA encryption giving junk characters


I am trying to encrypt and decrypt the XML file using AES and RSA but getting some junk characters after decryption.

package com.example.demo.junk;


import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.Duration;
import java.time.Instant;

public class EncryptionPOC {

    private static IvParameterSpec getIv() {
        SecureRandom srandom = new SecureRandom();
        byte[] iv = new byte[128 / 8];
        srandom.nextBytes(iv);
        IvParameterSpec ivspec = new IvParameterSpec(iv);
        //Save it.
        try (FileOutputStream fos = new FileOutputStream("iv.file")) {
            fos.write(iv);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return ivspec;
    }

    private static SecretKey getSecretKey(String algorithm) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance(algorithm);
            kgen.init(128);
            return kgen.generateKey();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    private static PrivateKey loadPrivateKey() {
        try {
            byte[] bytes = Files.readAllBytes(Paths.get("private.key"));
            PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(bytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PrivateKey pvt = kf.generatePrivate(ks);
            return pvt;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static KeyPair getKeyPair(String algorithm) {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm);
            kpg.initialize(2048);
            KeyPair keyPair = kpg.generateKeyPair();
            return keyPair;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    private static void writeContent(IvParameterSpec ivspec, PrivateKey privateKey, SecretKey secretKey) {

        try {
            FileOutputStream out = new FileOutputStream("Manifest-Encrypted.enc");
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            byte[] b = cipher.doFinal(secretKey.getEncoded());
            out.write(b);

            Cipher ci = Cipher.getInstance("AES/CBC/ISO10126Padding");
            ci.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
            try (FileInputStream in = new FileInputStream("Manifest1.xml")) {
                processFileForAes(ci, in, out);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void readContent() {

        try {
            byte[] bytes = Files.readAllBytes(Paths.get("public.pub"));
            X509EncodedKeySpec ks = new X509EncodedKeySpec(bytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PublicKey pub = kf.generatePublic(ks);

            FileInputStream in = new FileInputStream("Manifest-Encrypted.enc");
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, pub);
            byte[] b = new byte[256];
            in.read(b);
            byte[] keyb = cipher.doFinal(b);
            SecretKeySpec skey = new SecretKeySpec(keyb, "AES");

            byte[] iv = new byte[128 / 8];
            FileInputStream fis = new FileInputStream("iv.file");
            fis.read(iv);
            IvParameterSpec ivspec = new IvParameterSpec(iv);

            Cipher ci = Cipher.getInstance("AES/CBC/ISO10126Padding");
            ci.init(Cipher.DECRYPT_MODE, skey, ivspec);
            try (FileOutputStream out = new FileOutputStream("Manifest2.xml")) {
                processFile(ci, in, out);
                readFile("Manifest2.xml");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void readFile(String fileName) throws IOException {
        try(FileInputStream out = new FileInputStream(fileName)) {
            int i;

            while ((i = out.read()) != -1) {
                System.out.print((char) i);
            }
            System.out.println();
        }
    }


    //  ////////////////////////////////////////////////////////////////////////////////////////
    private static void saveKeypair(KeyPair keyPair) {
        try (FileOutputStream out = new FileOutputStream("private.key")) {
            out.write(keyPair.getPrivate().getEncoded());
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

        try (FileOutputStream out = new FileOutputStream("public.pub")) {
            out.write(keyPair.getPublic().getEncoded());
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


    public static void RSAEncryption(PrivateKey privateKey) {

        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);

            try (FileOutputStream out = new FileOutputStream("Doc-enc.des");
                 FileInputStream in = new FileInputStream("File1.deb")) {
                processFile(cipher, in, out);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    private static void processFile(Cipher ci, InputStream in, OutputStream out)
            throws javax.crypto.IllegalBlockSizeException,
            javax.crypto.BadPaddingException,
            java.io.IOException {
        byte[] ibuf = new byte[1024];
        int len;
        while ((len = in.read(ibuf)) != -1) {
            byte[] obuf = ci.update(ibuf, 0, len);
            if (obuf != null)
            {
                out.write(obuf);
            }
        }
        byte[] obuf = ci.doFinal();
        if (obuf != null) out.write(obuf);
    }


    static private void processFileForAes(Cipher ci, InputStream in, OutputStream out)
            throws javax.crypto.IllegalBlockSizeException,
            javax.crypto.BadPaddingException,
            java.io.IOException {
        byte[] ibuf = new byte[1024];
        int len;
        while ((len = in.read(ibuf)) != -1) {
            byte[] obuf = ci.update(ibuf, 0, len);
            if (obuf != null) out.write(obuf);
        }
        byte[] obuf = ci.doFinal();
        if (obuf != null) out.write(obuf);
    }

    public static void main(String[] args) {
//      Instant in1 = Instant.now();
//      KeyPair keyPair = getKeyPair("RSA");
//      saveKeypair(keyPair);
//      RSAEncryption(keyPair.getPrivate());
//      Instant in2 = Instant.now();
//
//      Duration duration = Duration.between(in1, in2);
//      System.out.println(duration.toMillis());

        //KeyPair keyPair = getKeyPair("RSA");
        //saveKeypair(keyPair);
//        Instant in1 = Instant.now();
//        SecretKey secretKey = getSecretKey("AES");
//        IvParameterSpec ivs = getIv();
//        PrivateKey privateKey = loadPrivateKey();
//        writeContent(ivs, privateKey, secretKey);
//        Instant in2 = Instant.now();
        readContent();
        //Duration duration = Duration.between(in1, in2);
        //System.out.println(duration.toMillis());

    }

}

Output:

** �#y`�X�&f(�{��declaration">1176**

For some files, I am getting correct output but for some, it is giving junk characters in beginning. What could be the possible root cause of this issue?


Solution

  • The main issue is that you seem to write the IV to a separate file when you create your ciphertext, and assume that is written to the front of the file when deciphering it.

    Furthermore, even if you use the streaming API to get to the content bytes from the right location, you should be aware that Java InputStream.read() may not read a buffer-worth of bytes. Instead you should use InputStream#readNBytes(int) to get more concise code as well.

    I'd also strongly advice you to handle your exceptions appropriately and use try-with-resources.