英文:
How to properly asymmetrically encrypt and decrypt over sockets java
问题
public class EchoClient {
private Socket clientSocket;
private DataOutputStream out;
private DataInputStream in;
private PrivateKey generateKeyPairClient() throws NoSuchAlgorithmException {
// ... (same code)
return kp.getPrivate();
}
private PublicKey obtainServerPublicKey() {
// ... (same code)
return serverPublicKey;
}
private void startConnection(String ip, int port) {
// ... (same code)
}
private void sendPlainTextToEncryptedText(String original, PublicKey serverPublicKey, PrivateKey clientPrivateKey) {
// ... (same code)
}
private void stopConnection() {
// ... (same code)
}
public static void main(String[] args) throws NoSuchAlgorithmException {
// ... (same code)
}
}
public class EchoServer {
private ServerSocket serverSocket;
private Socket clientSocket;
private DataOutputStream out;
private DataInputStream in;
private PrivateKey generateKeyPairServer() throws NoSuchAlgorithmException {
// ... (same code)
return kp.getPrivate();
}
private PublicKey obtainClientPublicKey() {
// ... (same code)
return clientPublicKey;
}
public void start(int port) {
// ... (same code)
}
private void receiveEncryptedTextToPlainText(PrivateKey serverPrivateKey) {
// ... (same code)
}
public void stop() {
// ... (same code)
}
public static void main(String[] args) throws NoSuchAlgorithmException {
// ... (same code)
}
}
import java.io.UnsupportedEncodingException;
public class Util {
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString();
}
public static String strToHex(String s) {
s = "failed decoding";
try {
s = Util.bytesToHex(s.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
System.out.println("Unsupported Encoding Exception");
}
return s;
}
}
Console outputs:
Image links removed for translation clarity.
英文:
I want to be able to encrypt and decrypt strings over a simple client and server using an echo model architecture with a specified port.
The way the client and server communicate is as follows:
The client is run first and generates a key pair, prints out its public key to the console, and waits for an input (which will be server's public key via copy and paste)
The server is then run and does the same also. Printing out its public key and waits for the client's public key (also copy and paste.)
Copy and paste (client public key) is done to the server first, which then starts the server listening on port 4444.
The client is then also started with copy and paste (server public key), sending a string over port 4444.
encrypted with the server’s public key.
The strings are to be encrypted by the client, sent, and received by the server who will decrypt the strings. An additional feature I am thinking to add should allow multiple strings to be encrypted and decrypted (done by a while loop).
My console for the client seems to be able to send the strings (converted to bytes, then sent) just fine by using out.write() and out.flush()
, but has trouble reading the encrypted bytes using (while data is not empty... in.read()
.
The exception thrown is Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
My client code:
public class EchoClient {
private Socket clientSocket;
private DataOutputStream out;
private DataInputStream in;
private PrivateKey generateKeyPairClient() throws NoSuchAlgorithmException {
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
final KeyPair kp = kpg.generateKeyPair();
final PublicKey clientPublicKey = kp.getPublic();
System.out.println(clientPublicKey);
System.out.println("Client Public Key (Encoded) is " + Base64.getEncoder().encodeToString(clientPublicKey.getEncoded()));
return kp.getPrivate();
}
private PublicKey obtainServerPublicKey() {
System.out.println("Please enter the servers's public key below:");
Scanner sc = new Scanner(System.in);
String key = sc.next();
sc.close();
PublicKey serverPublicKey = null;
try{
byte[] byteKey = Base64.getDecoder().decode(key.getBytes());
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
serverPublicKey = kf.generatePublic(X509publicKey);
}
catch(Exception e){
System.out.println("The public key you entered for the server is not valid, please try again");
}
return serverPublicKey;
}
private void startConnection(String ip, int port) {
try {
clientSocket = new Socket(ip, port);
out = new DataOutputStream(clientSocket.getOutputStream());
in = new DataInputStream(clientSocket.getInputStream());
} catch (IOException e) {
System.out.println("Error when initializing connection");
}
}
private void sendPlainTextToEncryptedText(String original, PublicKey serverPublicKey, PrivateKey clientPrivateKey) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, serverPublicKey);
byte[] originalBytes = original.getBytes(StandardCharsets.UTF_8);
byte[] cipherTextBytes = cipher.doFinal(originalBytes);
System.out.println("Client will send the ciphertext: "+ Util.bytesToHex(cipherTextBytes));
out.write(cipherTextBytes);
out.flush();
} catch (Exception e) {
System.out.println("There was a problem when trying to encrypt/decrypt, please try again");
e.printStackTrace();
return;
}
}
private void stopConnection() {
try {
in.close();
out.close();
clientSocket.close();
} catch (IOException e) {
System.out.println("error when closing");
}
}
public static void main(String[] args) throws NoSuchAlgorithmException {
EchoClient client = new EchoClient();
PrivateKey clientPrivateKey = client.generateKeyPairClient();
PublicKey serverPublicKey = client.obtainServerPublicKey();
System.out.println("Key Exchange Complete for Client");
client.startConnection("127.0.0.1", 4444);
client.sendPlainTextToEncryptedText("everyone", serverPublicKey, clientPrivateKey);
client.stopConnection();
}
}
My server code:
public class EchoServer {
private ServerSocket serverSocket;
private Socket clientSocket;
private DataOutputStream out;
private DataInputStream in;
private PrivateKey generateKeyPairServer() throws NoSuchAlgorithmException {
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
final KeyPair kp = kpg.generateKeyPair();
final PublicKey serverPublicKey = kp.getPublic();
System.out.println(serverPublicKey);
System.out.println("Server Public Key (Encoded) is " + Base64.getEncoder().encodeToString(serverPublicKey.getEncoded()));
return kp.getPrivate();
}
private PublicKey obtainClientPublicKey() {
System.out.println("Please enter the clients's public key below:");
Scanner sc = new Scanner(System.in);
String key = sc.next();
sc.close();
PublicKey clientPublicKey = null;
try{
byte[] byteKey = Base64.getDecoder().decode(key.getBytes());
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
clientPublicKey = kf.generatePublic(X509publicKey);
}
catch(Exception e){
System.out.println("The public key you entered for the client is not valid, please try again");
}
return clientPublicKey;
}
public void start(int port) {
try {
serverSocket = new ServerSocket(port);
clientSocket = serverSocket.accept();
out = new DataOutputStream(clientSocket.getOutputStream());
in = new DataInputStream(clientSocket.getInputStream());
} catch (IOException e) {
System.out.println("Error when establishing/accepting server connection");
}
}
private void receiveEncryptedTextToPlainText(PrivateKey serverPrivateKey) throws UnsupportedEncodingException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] data = new byte[8];
@SuppressWarnings("unused")
int numBytes;
while ((numBytes = in.read(data)) != -1) {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, serverPrivateKey);
byte[] decryptedBytes = cipher.doFinal(data);
String text = new String(decryptedBytes, StandardCharsets.UTF_8);
System.out.println("Server has recieved the plain text: "+ text);
}
stop();
}
public void stop() {
try {
in.close();
out.close();
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, UnsupportedEncodingException, IOException, IllegalBlockSizeException, BadPaddingException {
EchoServer server = new EchoServer();
PrivateKey serverPrivateKey = server.generateKeyPairServer();
//Sorry for the mis-named var, will rename in code
PublicKey serverPublicKey = server.obtainClientPublicKey();
System.out.println("Key Exchange Complete for Server");
System.out.println("Now waiting for incoming connection...");
server.start(4444);
server.receiveEncryptedTextToPlainText(serverPrivateKey);
server.stop();
}
}
The Util assist class
import java.io.UnsupportedEncodingException;
public class Util {
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString();
}
public static String strToHex(String s) {
s = "failed decoding";
try {
s = Util.bytesToHex(s.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
System.out.println("Unsupported Encoding Exception");
}
return s;
}
}
The console outputs:
I'm not sure at to what might be wrong here, any advice?
I have noted two things which I think may be the cause:
Note: I am copying and pasting public keys (converting them to strings and back to public key objects - may be a problem...)
Note 2: The transfer of bytes over the socket in a size of 8 bytes might be problematic for longer strings
答案1
得分: 3
你必须在解密之前读取所有的密文数据。在 EchoServer
中:
private void receiveEncryptedTextToPlainText(PrivateKey serverPrivateKey) throws UnsupportedEncodingException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] data = new byte[8];
ByteArrayOutputStream cipherData = new ByteArrayOutputStream();
@SuppressWarnings("unused")
int numBytes;
while ((numBytes = in.read(data)) != -1) {
cipherData.write(data, 0, numBytes);
}
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, serverPrivateKey);
byte[] decryptedBytes = cipher.doFinal(cipherData.toByteArray());
String text = new String(decryptedBytes, StandardCharsets.UTF_8);
System.out.println("服务器已接收到纯文本:" + text);
stop();
}
英文:
You must read all cipher-data before decrypt it. At EchoServer
:
private void receiveEncryptedTextToPlainText(PrivateKey serverPrivateKey) throws UnsupportedEncodingException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] data = new byte[8];
ByteArrayOutputStream cipherData = new ByteArrayOutputStream();
@SuppressWarnings("unused")
int numBytes;
while ((numBytes = in.read(data)) != -1) {
cipherData.write(data, 0, numBytes);
}
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, serverPrivateKey);
byte[] decryptedBytes = cipher.doFinal(cipherData.toByteArray());
String text = new String(decryptedBytes, StandardCharsets.UTF_8);
System.out.println("Server has recieved the plain text: " + text);
stop();
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论