如何在不丢失或更改任何数据的情况下安全地将 RSA 加密和解密的字节转换为字符串?

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

标题已编辑。原标题:我在我的项目中使用RSA加密,在Eclipse中编译时它完美运行,但是当我运行它使用bat文件时,我得到一个错误的填充错误

我正在为我的项目创建一个网络库,用于创建客户端和服务器。我为握手部分添加了 RSA 加密,但 RSA 存在问题。它在 Eclipse 中工作得很好,但是一旦我将它导出为 运行nable 并在命令提示符下将其导出为 运行,我就会收到以下错误:

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)

这是我用来生成 key-pair 的内容:(使用 2048 位)

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();
}

这里是我用来加解密的:

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);
}

堆栈跟踪指向解密方法中的 return 语句。

当我编译它时,这一切都在 Eclipse 中完美运行,但是当我 运行 它使用 CMD 或作为任何 java 运行nable 的库时,它给出了这个确切的错误.

编辑:我已经确定了确切的问题。我有一个使用 RSA 加密和解密但输出字符串而不是字节的方法。以下是我使用的仅在 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;
}

我在下面贴出了自己的解决方案,但是我的解决方案非常糟糕而且效率低下。

我通过更改将加密字节转换为字符串以及将解密字节转换为字符串的方式来实现这一点。这种方法很糟糕,但它在 eclipse 内外都没有任何运行时异常。

我像这样制作了加密和解密方法(return 字符串):

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));
}

这是非常低效的,所以我仍在寻找一种更好的方法来将字节转换为字符串并返回,而不会丢失或更改任何数据。

您最初的 StringBytes 转换通常仅在单个 Java 过程中起作用(尽管无用)。因为 byte 在 Java 中签名,它将创建一个 String 包含具有 Unicode 代码点 U+0000 到 U+007F 的字符,这些字符大部分是相当可移植的(尽管一些控制字符不会在 Windows) 和 U+FF80 到 U+FFFF 上始终如一地工作,如果发送到进程之外通常会被销毁。

对于网络传输,这似乎是您的目标,您根本不应该转换。 Java 网络(直接)仅支持 TCP/IP 堆栈,它直接支持(任何)字节的传输——不是字符,包括 Java String,它由字符而不是字节组成。

对于其他类型的传输或存储,您需要使用媒体或渠道支持的字符。包括 RSA 在内的加密数据只是可以使用所有 256 字节值的位,而您可能想要使用的许多媒体和通道并不支持所有 256 字节值,因此通常使用更受限制的字符集对加密数据进行编码,如十六进制或几种 base64 或 base95, 可以安全传输或存储。 Stack 上可能有数百个关于此类编码的 Q,并且至少有数千个网站在讨论它们。