[英]How to properly asymmetrically encrypt and decrypt over sockets java
我希望能夠使用具有指定端口的回聲模型體系結構在簡單的客戶端和服務器上加密和解密字符串。
客戶端和服務器的通信方式如下:
客戶端首先運行並生成密鑰對,將其公鑰打印到控制台,然后等待輸入(通過復制和粘貼將成為服務器的公鑰)
然后運行服務器並執行相同的操作。 打印出它的公鑰並等待客戶端的公鑰(也復制和粘貼。)
首先復制和粘貼(客戶端公鑰)到服務器,然后啟動服務器偵聽端口 4444。
然后客戶端也開始復制和粘貼(服務器公鑰),通過端口 4444 發送一個字符串。用服務器的公鑰加密。
字符串將由客戶端加密,由將解密字符串的服務器發送和接收。 我想添加的附加功能應該允許對多個字符串進行加密和解密(通過 while 循環完成)。
我的客戶端控制台似乎能夠通過使用out.write() and out.flush()
很好地發送字符串(轉換為字節,然后發送out.write() and out.flush()
,但是使用讀取加密字節時遇到問題(而數據不是空... in.read()
。
拋出的Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
我的客戶代碼:
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();
}
}
我的服務器代碼:
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();
}
}
Util 輔助類
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;
}
}
控制台輸出:
我不確定這里可能有什么問題,有什么建議嗎?
我注意到了兩件事,我認為這可能是原因:
注意:我正在復制和粘貼公鑰(將它們轉換為字符串並返回公鑰對象 - 可能是一個問題......)
注 2:對於較長的字符串,通過套接字以 8 字節大小傳輸字節可能會出現問題
您必須在解密之前閱讀所有密碼數據。 在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();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.