我的服务器在 Intelij Idea Windows 上,客户端在 Android 上。我将 rsa 公钥从服务器发送到客户端,客户端加密公钥消息并发送给服务器。解密消息时服务器出错。
我尝试不在 Intelij 上使用服务器,而是在 Android 上使用服务器,一切正常。我也尝试交换服务器和客户端,服务器 - Android,客户端 - Intelij,也没有成功,但结果不同:服务器获取并成功解密消息,但消息中还有许多其他符号。
这是代码,其中服务器 - Intelij 和客户端 - Android。
服务器:
ServerSocket serverSocket = new ServerSocket(5556);
Socket socket = serverSocket.accept();
System.out.println("Client connected");
DataOutputStream dOut = new DataOutputStream(socket.getOutputStream());
DataInputStream dIn = new DataInputStream(socket.getInputStream());
//Create keys
Map<String, Object> RsaKey = rsa.initKey();
String RsaPrivateKey = rsa.getPrivateKey(RsaKey);
String RsaPublicKey = rsa.getPublicKey(RsaKey);
//send key
dOut.writeInt(RsaPublicKey.getBytes().length);
dOut.write(RsaPublicKey.getBytes());
Thread.sleep(1000);
System.out.println(RsaPublicKey);
//read encrypted message
int length_pk = dIn.readInt();
byte[] message = new byte[length_pk];
dIn.readFully(message, 0, message.length);
String st1 = new String(message);
System.out.println(st1 + " " + length_pk + " " + st1.length());
String str = new String(rsa.decryptByPrivateKey(message, RsaPrivateKey));
System.out.println(str);
客户:
Socket client = new Socket("192.168.1.103", 5556);
DataOutputStream dOut = new DataOutputStream(client.getOutputStream());
DataInputStream dIn = new DataInputStream(client.getInputStream());
//read key from server
int length_pk1 = dIn.readInt();
byte[] publickeyserver1 = new byte[length_pk1];
dIn.readFully(publickeyserver1, 0, publickeyserver1.length);
String RsaPublicKeyServer = new String(publickeyserver1);
//test key rsa
String str = "Who is there?";
byte[] encbyte = rsa.encryptByPublicKey(str.getBytes(), RsaPublicKeyServer);
dOut.writeInt(encbyte.length);
dOut.write(encbyte);
Rsa 库:
public static final String KEY_ALGORITHM = "RSA";
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
private static final String PUBLIC_KEY = "RSAPublicKey";
private static final String PRIVATE_KEY = "RSAPrivateKey";
public static byte[] decryptBASE64(String key) throws Exception {
return Base64.getDecoder().decode(key);
}
public static byte[] decryptByPrivateKey(byte[] data, String key)throws Exception {
byte[] keyBytes = decryptBASE64(key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptBASE64(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
public static String getPrivateKey(Map<String, Object> keyMap)throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return encryptBASE64(key.getEncoded());
}
public static String getPublicKey(Map<String, Object> keyMap)throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return encryptBASE64(key.getEncoded());
}
public static Map<String, Object> initKey() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
堆栈跟踪
线程“main”java.lang.RuntimeException 中出现异常: javax.crypto.BadPaddingException:解密错误 Main.main(Main.java:110) 引起:javax.crypto.BadPaddingException: 解密错误位于 java.base/sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:369) 在 java.base/sun.security.rsa.RSAPadding.unpad(RSAPadding.java:282) 在 java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:366) 在 java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:400) 在 java.base/javax.crypto.Cipher.doFinal(Cipher.java:2206) 处 main.java.rsa.decryptByPrivateKey(rsa.java:134) 在 Main.main(Main.java:103)
我已经使用Rsa库很长时间了,我不认为这是一个问题。我认为问题出在IDE之间。
正如我在评论中所建议的那样,问题是您未能将完整的“算法/模式/填充”转换规范指定为
Cipher.getInstance()
。相反,您使用 keyFactory.getAlgorithm()
作为规范。 KeyFactory 算法恰好返回“RSA”,当您使用 Cipher.getInstance("RSA")
时,您将以特定于提供者的默认值结束。在 Android 上,填充默认值为“NoPadding”,在 Java 上,填充默认值为“PKCS1Padding”。两边都使用RSA/ECB/PKCS1Padding会更好。比这更好的是更现代但更复杂的“RSA/ECB/OAEPwithSHA-256andMGF1Padding”。OAEPwithSHA-256andMGF1Padding
方案本身有参数,尽管您可以使用默认值,但它们是特定于提供者的,因此应该避免。返回一个初始化的、可互操作的、基于现代 RSA 的 Cipher
实例,其中包含几行代码:
private static final String RSA_CIPHER_SPEC = "RSA/ECB/OAEPwithSHA-256andMGF1Padding";
private static Cipher getRsaCipher(int direction, Key key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException {
Cipher cipher = Cipher.getInstance(RSA_CIPHER_SPEC);
OAEPParameterSpec oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
cipher.init(direction, key, oaepSpec);
return cipher;
}
这可以在您的加密/解密方法中使用,如下所示:
public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptBASE64(key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = getRsaCipher(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptBASE64(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = getRsaCipher(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
Android 和 Java 端。
还可以对您的代码进行其他改进,例如消除 Base64 编码/解码。这似乎增加了并发症,却没有任何好处。