如何正确地在Java中使用非对称加密和解密套接字。

huangapple go评论69阅读模式
英文:

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:

如何正确地在Java中使用非对称加密和解密套接字。

如何正确地在Java中使用非对称加密和解密套接字。

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

huangapple
  • 本文由 发表于 2020年10月4日 16:05:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/64192400.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定