3

Title was edited. Original title: I use RSA encryption in my project, and it works perfectly when compiled in Eclipse, but when I run it using a bat file, I get a bad padding error

I am creating a network library for my projects which is used to create clients and servers. I added RSA encryption for the handshake portion, but there is an issue with the RSA. It works perfectly fine within Eclipse, but once I export it as a runnable and run it with command prompt, I get the following error:

javax.crypto.BadPaddingException: Decryption error[CLIENT] [Warning] Failed to get server response!

        at sun.security.rsa.RSAPadding.unpadV15(Unknown Source)
        at sun.security.rsa.RSAPadding.unpad(Unknown Source)
        at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
        at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
        at javax.crypto.Cipher.doFinal(Cipher.java:2165)
        at me.forseth11.networkutils.utils.crypto.RSA.decrypt(RSA.java:71)
        at me.forseth11.networkutils.utils.crypto.RSA.decrypt(RSA.java:37)
        at me.forseth11.networkutils.server.Receiver.processInput(Receiver.java:87)
        at me.forseth11.networkutils.server.Receiver.run(Receiver.java:36)

Here is what I use to generate the key-pair: (Using 2048 bits)

public static KeyPair generate(int bits) throws Exception {
    KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
    RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(bits,
            RSAKeyGenParameterSpec.F4);
    keygen.initialize(spec);
    return keygen.generateKeyPair();
}

Here is what I use for encryption and decryption:

public static byte[] encrypt(byte[] data, PublicKey key) throws Exception {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    return cipher.doFinal(data);
}

public static byte[] decrypt(byte[] data, PrivateKey key) throws Exception {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, key);
    return cipher.doFinal(data);
}

The stack trace points to the return statement within the decrypt method.

This all works PERFECTLY inside Eclipse when I compile it, but when I run it using CMD or as a library for any java runnable, it gives this exact error.

EDIT: I have identified the exact problem. I have a method which encrypts and decrypts using RSA but outputs a String instead of bytes. Here are the methods I used which worked only in eclipse:

public static String encrypt(String message, PublicKey publicKey) throws Exception {
    return StringBytes.getString(RSA.encrypt(message.getBytes(), publicKey));//Encrypt refers to encrypt method above
}

public static String decrypt(String message, PrivateKey privateKey) throws Exception {
    return new String(RSA.decrypt(StringBytes.getBytes(message), privateKey));
}

//StringBytes methods
public static byte[] getBytes(String message) {
    byte[] array = new byte[message.length()];
    for(int i = 0; i < message.length(); i++) {
        array[i] = (byte) message.charAt(i);
    }
    return array;
}

public static String getString(byte[] bytes) {
    String str = "";
    for(byte b : bytes) {
        System.out.println("Byte: " + b);
        str += ((char)b);
    }
    return str;
}

I posted my own solution below, but my solution is very bad and inefficient.

Forseth11
  • 1,418
  • 1
  • 12
  • 21
  • Possible duplicate of [BadPaddingException : Decryption error](https://stackoverflow.com/questions/31944374/badpaddingexception-decryption-error) – Sam Jan 30 '18 at 15:26
  • @Sam I tried that, but it did not work for me. Also, I am using 2048 bits, and that post does not explain anything about re-encoding the key from String to PublicKey via KeyFactory which may be the cause of the problem. – Forseth11 Jan 30 '18 at 15:34
  • First, you forgot to add your "exact error" string :p ;), second did you check your classpath is the same in both cases? – LMC Jan 30 '18 at 16:26
  • JCE works exactly the same in Eclipse or outside, possibly (if you have more than one JRE) modulo the 'policy' strength limit on _symmetric_ crypto (NOT RSA) until recently; I frequently mix them. Make certain the data your receiver is trying to decrypt is _exactly_ byte for byte the same as the sender's encryption output; since humans are bad at exactly checking random-looking data check a CRC or hash converted to human readable hex or b64 (since you're already coding JCE hash is likely easier). – dave_thompson_085 Jan 30 '18 at 16:43
  • @LuisMuñoz I did not think it was needed because the exact same code and key works within Eclipse 100% of the time out of about 10000 trials, and works 0% of the time outside of eclipse. Also the classpath is exactly the same in both. – Forseth11 Jan 30 '18 at 17:20
  • This is java, the issue is around the jvm version or the classpath. Are you running multiple versions of java? – LMC Jan 30 '18 at 17:24
  • @dave_thompson_085 I made this tester class to test the encryption, and it worked inside and outside of eclipse, so I must have a problem with converting to and from a string with the encrypted message: https://pastebin.com/txnbKU2g How could I convert a public key to a readable string that won't get corrupt with encodings? How would I do the same with an encrypted message via public key? – Forseth11 Jan 30 '18 at 17:36
  • @LuisMuñoz I only have java 8 installed. I just checked. – Forseth11 Jan 30 '18 at 17:37
  • make your app to log full classpath so you can compare. – LMC Jan 30 '18 at 17:40
  • @LuisMuñoz How would I go about doing that? – Forseth11 Jan 30 '18 at 17:41
  • check this https://www.mkyong.com/java/how-to-print-out-the-current-project-classpath/ – LMC Jan 30 '18 at 17:42
  • @LuisMuñoz Results: `/C:/Users/Micha/Documents/moltres%20workspace/NetworkUtils/bin/` `/C:/Users/Micha/Desktop/NetworkUtilsTest.jar` – Forseth11 Jan 30 '18 at 17:46
  • 1
    just 1 entry on each?! – LMC Jan 30 '18 at 17:48

2 Answers2

2

Your original StringBytes conversions will normally work (though uselessly) within a single Java process only. Because byte is signed in Java, it will create a String containing characters with Unicode codepoints U+0000 to U+007F, which are mostly fairly portable (though some control chars won't work consistently on Windows), and U+FF80 to U+FFFF, which will usually get destroyed if sent outside of the the process.

For transmission over a network, which appears to be your goal, you shouldn't convert at all. Java networking (directly) supports only the TCP/IP stack, which directly supports tranmission of (any) bytes -- NOT characters including Java String which consists of characters not bytes.

For other kinds of transmission or storage, you need to use characters supported by the medium or channel. Enciphered data, including RSA, is simply bits that can use all 256 byte values, and many mediums and channels you might want to use don't support all 256 byte values, so it is common to encode enciphered data in a more restricted character set, like hexadecimal or several kinds of base64 or base95, that is safe for transmission or storage. There are probably hundreds of Qs on Stack about such encodings, and at least thousands of websites discussing them.

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • I am using Strings because I had to make this project compatible with several existing projects. I am making a very which uses strictly byes, but I need String conversions for this one. Could you point me in the right direction on how to convert those encrypted/decrypted bytes to and from base64 to avoid corruption? – Forseth11 Jan 31 '18 at 01:10
  • @Forseth11 For base64 in java8 up it's easiest to use `java.util.Base64`; see the javadoc. It has `encodeToString` and an overload of `decode` that takes `String`. Before (or besides) that you could use `javax.xml.bind.DatatypeConverter`. – dave_thompson_085 Jan 31 '18 at 05:10
  • I used Base64 encode and decode and it worked perfectly! – Forseth11 Feb 01 '18 at 19:23
0

I have got this to work by changing how I convert encrypted bytes into a String and decrypted bytes into a String. This method is terrible, but it works without any runtime exceptions in or out of eclipse.

I made the encrypt and decrypt methods (which return String) like this:

public static String encrypt(String message, PublicKey publicKey) throws Exception {
    String s = "";
    for(byte b : RSA.encrypt(message.getBytes(), publicKey)) {
        s += b + ",";
    }
    return s;
}

public static String decrypt(String message, PrivateKey privateKey) throws Exception {
    String[] split = message.split(",");
    byte[] b = new byte[split.length];
    for(int i = 0; i < split.length; i++) {
        b[i] = Byte.parseByte(split[i]);
    }
    return new String(RSA.decrypt(b, privateKey));
}

This is very inefficient, so I am still looking for a better way to convert the bytes into a String and back without losing or changing any of the data.

Forseth11
  • 1,418
  • 1
  • 12
  • 21