Java 尝试发送使用AES加密的对象时出现问题
我在尝试通过CipherOutputStream发送对象以使用AES进行加密,并通过CipherInputStream接收对象以对其进行解密时遇到问题 问题是服务器无法接收对象:Java 尝试发送使用AES加密的对象时出现问题,java,sockets,object,cryptography,aes,Java,Sockets,Object,Cryptography,Aes,我在尝试通过CipherOutputStream发送对象以使用AES进行加密,并通过CipherInputStream接收对象以对其进行解密时遇到问题 问题是服务器无法接收对象: Client > INFO > created ObjectOutputStream Client > INFO > sent a Person object through ObjectOutputStream Server > INFO > created cipherInput
Client > INFO > created ObjectOutputStream
Client > INFO > sent a Person object through ObjectOutputStream
Server > INFO > created cipherInputStream
Server > INFO > created ObjectInputStream
(它在此之前阻塞:)
如果我直接使用ObjectOutputStream和ObjectInputStream,而不是客户端中的CipherOutputStream和服务器中的CipherInputStream,那么它会正确地接收到它(我知道,因为我尝试过)
您是否有其他方法建议发送AES加密对象或如何解决这个问题
提前谢谢
主要
客户
import javax.crypto.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.security.*;
import java.util.Arrays;
public class Client {
private final int port = 3535;
private PublicKey publicKey;
private PrivateKey privateKey;
private SecretKey aesKey;
public Client(String hostname) {
try {
Socket socket = new Socket(hostname, port);
System.out.println("Client > INFO > Connected to server: " + socket.toString());
KeyPair keyPair = Cryptography.rsaKey();
if(keyPair == null) {
System.out.println("Client > ERROR > keyPair is null!");
return;
}
publicKey = keyPair.getPublic();
privateKey = keyPair.getPrivate();
//System.out.println("Client > publicKey > " + publicKey.toString());
//System.out.println("Client > privateKey created");
aesKey = Cryptography.aesKey();
if(aesKey == null) {
System.out.println("Client > ERROR > aesKey is null!");
return;
}
System.out.println("Client > INFO > aesKey > " + Arrays.toString(aesKey.getEncoded()));;
new Thread(new Runnable() {
@Override
public void run() {
try {
// Receiving the RSA - PublicKey
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
System.out.println("Client > INFO > created objectInputStream: " + objectInputStream.toString());
PublicKey publicKey1 = (PublicKey) objectInputStream.readObject();
System.out.println("Client > INFO > publicKey received from server: " + publicKey1.toString());
// Creating a Cipher object
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Initializing the Cipher object for encrypting
rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey1);
byte[] encryptedKey = rsaCipher.doFinal(aesKey.getEncoded());
//System.out.println("Client > INFO > sending encrypted aesKey: " + Arrays.toString(encryptedKey));
// Sending the AES - SecretKey encrypted with the RSA - PublicKey
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
dataOutputStream.writeInt(encryptedKey.length);
dataOutputStream.flush();
dataOutputStream.write(encryptedKey);
dataOutputStream.flush();
// Creating an ObjectOutputStream over a CipherOutputStream
Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey);
Person p = new Person();
p.setName("John");
p.setSurname("Smith");
CipherOutputStream cipherOutputStream = new CipherOutputStream(socket.getOutputStream(), aesCipher);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(cipherOutputStream);
System.out.println("Client > INFO > created ObjectOutputStream");
objectOutputStream.writeObject(p);
objectOutputStream.flush();
System.out.println("Client > INFO > sent a Person object through ObjectOutputStream");
} catch (IOException | ClassNotFoundException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.*;
import java.util.Arrays;
public class Server {
private final int port = 3535;
public ServerSocket serverSocket;
private PublicKey publicKey;
private PrivateKey privateKey;
public Server() {
try {
serverSocket = new ServerSocket(port);
KeyPair keyPair = Cryptography.rsaKey();
if(keyPair == null) {
System.out.println("Server > ERROR > keyPair is null!");
return;
}
publicKey = keyPair.getPublic();
privateKey = keyPair.getPrivate();
System.out.println("Server > publicKey > " + publicKey.toString());
System.out.println("Server > privateKey created");
ServerSocket finalServerSocket = serverSocket;
new Thread(new Runnable() {
@Override
public void run() {
Socket socket;
try {
socket = finalServerSocket.accept();
System.out.println("Server > INFO > A client connected: " + socket.toString());
// Sending the RSA - PublicKey
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
System.out.println("Server > INFO > created objectOutputStream: " + objectOutputStream.toString());
objectOutputStream.writeObject(publicKey);
objectOutputStream.flush();
// Receiving the encrypted AES - SecretKey
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
int length = dataInputStream.readInt();
System.out.println("Server > INFO > received encrypted aesKey's length: " + length);
byte[] encryptedKey;
if(length > 0) {
encryptedKey = new byte[length];
dataInputStream.readFully(encryptedKey, 0, encryptedKey.length);
} else {
System.out.println("Server > ERROR > length received is <= 0");
return;
}
System.out.println("Server > INFO > received encrypted aesKey: " + Arrays.toString(encryptedKey));
// Decrypting the encrypted AES - SecretKey
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedKey = rsaCipher.doFinal(encryptedKey);
// Converting the decrypted AES - SecretKey to a SecretKey
SecretKey aesKey = new SecretKeySpec(decryptedKey, 0, decryptedKey.length, "AES");
System.out.println("Server > INFO > converted decrypted aesKey: " + Arrays.toString(aesKey.getEncoded()));
Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
aesCipher.init(Cipher.DECRYPT_MODE, aesKey);
// Creating an ObjectInputStream over a CipherInputStream
CipherInputStream cipherInputStream = new CipherInputStream(socket.getInputStream(), aesCipher);
System.out.println("Server > INFO > created cipherInputStream");
ObjectInputStream objectInputStream = new ObjectInputStream(cipherInputStream);
System.out.println("Server > INFO > created ObjectInputStream");
Person p = (Person) objectInputStream.readObject();
System.out.println("Server > INFO > received a Person object: " + p.toString());
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | ClassNotFoundException ex) {
ex.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
密码学
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
public class Cryptography {
public static KeyPair rsaKey() {
try {
// Creating a Signature object
Signature sign = Signature.getInstance("SHA256withRSA");
// Creating KeyPair generator object
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// Initializing the key pair generator
keyPairGen.initialize(2048);
// Generating the pair of keys
return keyPairGen.generateKeyPair();
} catch (NoSuchAlgorithmException ignored) {}
return null;
}
public static SecretKey aesKey() {
try {
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(128); // The AES key size in number of bits
return generator.generateKey();
} catch (NoSuchAlgorithmException ignored) {}
return null;
}
}
首先:感谢您为我的项目提供了很好的示例:-) 代码中的问题出现在客户端,因为您没有关闭ObjectOutputStream。刷新后添加一行代码:
objectOutputStream.writeObject(p);
objectOutputStream.flush();
// new line
objectOutputStream.close();
让示例按预期运行:
...
Server > INFO > created cipherInputStream
Server > INFO > created ObjectInputStream
Server > INFO > received a Person object: Person{name='John', surname='Smith'}
编辑1:
正如您在问题中所写,完整的工作流运行时不使用CipherOutput/InputStream,因此该行为的原因在于
CipherOutputStream
请参阅Javadocs(),您会发现:
通过强制任何已由封装密码处理的缓冲输出字节来刷新此输出流
要写出的对象。由封装密码缓冲并等待其处理的任何字节将不会被写出
例如,如果封装的密码是分组密码,则使用其中一种写入方法写入的总字节数为
小于密码的块大小,将不会写出任何字节
作为解决方案(取决于数据量),我建议在客户端对内存中的数据进行加密(序列化后),并将加密数据作为字节[]传递给服务器端的dataOutputStream.write,反之亦然。这样,您就不需要关闭objectOutputStream,并且套接字仍为下一次传输打开。不客气,我很高兴它对您有所帮助。关于你的解决方案,它是有效的,谢谢你,但我想继续使用流和套接字。。。ObjectOutputStream#close方法将其关闭,您还有其他选择吗?
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
public class Cryptography {
public static KeyPair rsaKey() {
try {
// Creating a Signature object
Signature sign = Signature.getInstance("SHA256withRSA");
// Creating KeyPair generator object
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// Initializing the key pair generator
keyPairGen.initialize(2048);
// Generating the pair of keys
return keyPairGen.generateKeyPair();
} catch (NoSuchAlgorithmException ignored) {}
return null;
}
public static SecretKey aesKey() {
try {
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(128); // The AES key size in number of bits
return generator.generateKey();
} catch (NoSuchAlgorithmException ignored) {}
return null;
}
}
objectOutputStream.writeObject(p);
objectOutputStream.flush();
// new line
objectOutputStream.close();
...
Server > INFO > created cipherInputStream
Server > INFO > created ObjectInputStream
Server > INFO > received a Person object: Person{name='John', surname='Smith'}