简体   繁体   中英

How to safely convert RSA encrypted and decrypted bytes into a String without losing or changing any data?

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM