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

时间:2018-01-30 15:06:58

标签: java eclipse encryption character-encoding rsa

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

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

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)

以下是我用来生成密钥对的内容:(使用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);
}

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

当我编译它时,这一切都完全在Eclipse内部工作,但是当我使用CMD或任何java runnable的库运行它时,它会给出这个确切的错误。

编辑:我已经确定了确切的问题。我有一个方法,使用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;
}

我在下面发布了自己的解决方案,但我的解决方案非常糟糕且效率低下。

2 个答案:

答案 0 :(得分:2)

您原来的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,并且至少有数千个网站在讨论它们。

答案 1 :(得分:0)

我通过改变将加密字节转换为字符串并将解密字节转换为字符串的方式来实现此目的。这种方法很糟糕,但它可以在eclipse中没有任何运行时异常。

我做了加密和解密方法(返回String),如下所示:

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

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