Ello伙伴们!我正在尝试制作一个使用AES加密的聊天程序,但我遇到了一个问题。当我尝试运行程序时,它会给出标题中可以看到的错误。我会给你两个类的代码,因为看起来,这是完全诊断问题的唯一方法。有许多重复,但似乎没有回答我的具体问题。感谢你的帮助! 客户端:
package Chat.Application;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import java.security.spec.AlgorithmParameterSpec;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Encoder;
/**
* A simple Swing-based client for the chat server. Graphically it is a frame
* with a text field for entering messages and a textarea to see the whole
* dialog.
*
* The client follows the Chat Protocol which is as follows. When the server
* sends "SUBMITNAME" the client replies with the desired screen name. The
* server will keep sending "SUBMITNAME" requests as long as the client submits
* screen names that are already in use. When the server sends a line beginning
* with "NAMEACCEPTED" the client is now allowed to start sending the server
* arbitrary strings to be broadcast to all chatters connected to the server.
* When the server sends a line beginning with "MESSAGE " then all characters
* following this string should be displayed in its message area.
*/
public class ChatClient {
BufferedReader in;
PrintWriter out;
JFrame frame = new JFrame("ELECTRON Chatroom");
JTextField textField = new JTextField(40);
JTextArea messageArea = new JTextArea(8, 40);
Cipher cipher;
// password for encryption
final String strPassword = "1234567890123456";
// put this as key in AES
final SecretKeySpec key = new SecretKeySpec(strPassword.getBytes(), "AES");
/**
* Constructs the client by laying out the GUI and registering a listener
* with the textfield so that pressing Return in the listener sends the
* textfield contents to the server. Note however that the textfield is
* initially NOT editable, and only becomes editable AFTER the client
* receives the NAMEACCEPTED message from the server.
*/
public ChatClient() {
// Layout GUI
textField.setEditable(false);
messageArea.setEditable(false);
messageArea.setWrapStyleWord(true);
messageArea.setLineWrap(true);
frame.getContentPane().add(textField, "North");
frame.getContentPane().add(new JScrollPane(messageArea), "Center");
frame.pack();
// Add Listeners
textField.addActionListener(new ActionListener() {
/**
* Responds to pressing the enter key in the textfield by sending
* the contents of the text field to the server. Then clear the text
* area in preparation for the next message.
*/
@Override
public void actionPerformed(ActionEvent e) {
try {
String input = (textField.getText());
//ENCRYPTION
// Parameter specific algorithm
AlgorithmParameterSpec paramSpec = new IvParameterSpec(strPassword.getBytes());
//Whatever you want to encrypt/decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// You can use ENCRYPT_MODE (ENCRYPTunderscoreMODE) or DECRYPT_MODE (DECRYPT underscore MODE)
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
// encrypt data
byte[] encrypted = cipher.doFinal(input.getBytes());
// encode data using standard encoder
//String output = new BASE64Encoder().encode(encrypted);
System.out.println("Orginal tring: " + input);
System.out.println("Encrypted string: " + encrypted);
textField.setText("");
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException ex) {
Logger.getLogger(ChatClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
/**
* Prompt for and return the address of the server.
*/
private String getServerAddress() {
return JOptionPane.showInputDialog(
frame,
"Enter IP Address of the Server:",
"ELECTRON Chatroom",
JOptionPane.QUESTION_MESSAGE);
}
/**
* Prompt for and return the desired screen name.
*/
private String getName() {
return JOptionPane.showInputDialog(
frame,
"Choose a screen name:",
"Screen name selection",
JOptionPane.PLAIN_MESSAGE);
}
/**
* Connects to the server then enters the processing loop.
*/
private void run() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
// Make connection and initialize streams
String serverAddress = getServerAddress();
Socket socket = new Socket(serverAddress, 9001);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Process all messages from server, according to the protocol.
while (true) {
String line = in.readLine();
if (line.startsWith("SUBMITNAME")) {
out.println(getName());
} else if (line.startsWith("NAMEACCEPTED")) {
textField.setEditable(true);
} else if (line.startsWith("MESSAGE")) {
//DECRYPTION
line = line.substring(8);
System.out.println(line);
// Parameter specific algorithm
AlgorithmParameterSpec paramSpec = new IvParameterSpec(strPassword.getBytes());
//Whatever you want to encrypt/decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// You can use ENCRYPT_MODE (ENCRYPTunderscoreMODE) or DECRYPT_MODE (DECRYPT underscore MODE)
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
byte messageByte[] = cipher.doFinal(line.getBytes());
String message = new String(messageByte, "UTF-8");
messageArea.append(message.substring(8) + "\n");
System.out.println(message);
}
}
}
/**
* Runs the client as an application with a closeable frame.
*/
public static void main(String[] args) throws Exception {
ChatClient client = new ChatClient();
client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.frame.setVisible(true);
client.run();
}
}
服务器:
package Chat.Application;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
/**
* A multi-threaded chat room server. When a client connects the
* server requests a screen name by sending the client the
* text "SUBMITNAME", and keeps requesting a name until
* a unique one is received. After a client submits a unique
* name, the server acknowledges with "NAMEACCEPTED". Then
* all messages from that client will be broadcast to all other
* clients that have submitted a unique screen name. The
* broadcast messages are prefixed with "MESSAGE ".
*
* Because this is just a teaching example to illustrate a simple
* chat server, there are a few features that have been left out.
* Two are very useful and belong in production code:
*
* 1. The protocol should be enhanced so that the client can
* send clean disconnect messages to the server.
*
* 2. The server should do some logging.
*/
public class ChatServer {
/**
* The port that the server listens on.
*/
private static final int PORT = 9001;
/**
* The set of all names of clients in the chat room. Maintained
* so that we can check that new clients are not registering name
* already in use.
*/
private static HashSet<String> names = new HashSet<String>();
/**
* The set of all the print writers for all the clients. This
* set is kept so we can easily broadcast messages.
*/
private static HashSet<PrintWriter> writers = new HashSet<PrintWriter>();
/**
* The application main method, which just listens on a port and
* spawns handler threads.
*/
public static void main(String[] args) throws Exception {
System.out.println("Chat Server Activated");
ServerSocket listener = new ServerSocket(PORT);
try {
while (true) {
new Handler(listener.accept()).start();
}
} finally {
listener.close();
}
}
/**
* A handler thread class. Handlers are spawned from the listening
* loop and are responsible for a dealing with a single client
* and broadcasting its messages.
*/
private static class Handler extends Thread {
private String name;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
private Integer length;
/**
* Constructs a handler thread, squirreling away the socket.
* All the interesting work is done in the run method.
*/
public Handler(Socket socket) {
this.socket = socket;
}
/**
* Services this thread's client by repeatedly requesting a
* screen name until a unique one has been submitted, then
* acknowledges the name and registers the output stream for
* the client in a global set, then repeatedly gets inputs and
* broadcasts them.
*/
public void run() {
try {
// Create character streams for the socket.
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Request a name from this client. Keep requesting until
// a name is submitted that is not already used. Note that
// checking for the existence of a name and adding the name
// must be done while locking the set of names.
while (true) {
out.println("SUBMITNAME");
/*Supposed to change anybody who tries to login as Admin into different name... "Supposed" to....
if (name.equals("Admin")) {
out.println("SUBMITNAME");
return;
}*/
name = in.readLine();
length = name.length();
if (length == 0) {
out.println("SUBMITNAME");
return;
}
if (name == "null") {
out.println("SUBMITNAME");
return;
}
synchronized (names) {
if (!names.contains(name)) {
names.add(name);
break;
}
}
}
// Now that a successful name has been chosen, add the
// socket's print writer to the set of all writers so
// this client can receive broadcast messages.
out.println("NAMEACCEPTED");
//Announces that user is Online
out.println("MESSAGE " + name + " is now Online");
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + " is now Online");
}
writers.add(out);
// Accept messages from this client and broadcast them.
// Ignore other clients that cannot be broadcasted to.
while (true) {
String input = in.readLine();
System.out.println(input);
if (input == null) {
return;
}
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
} catch (IOException e) {
System.out.println(e);
} finally {
// This client is going down! Remove its name and its print
// writer from the sets, and close its socket.
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + " is now Offline");
} if (name != null) {
names.remove(name);
}
if (out != null) {
writers.remove(out);
}
try {
socket.close();
} catch (IOException e) {
}
}
}
}
}
非常感谢您在编程时保存完整菜鸟的所有帮助!
- 银
答案 0 :(得分:1)
这可能是您将看到的最好,最有用的错误消息。它告诉你究竟是什么问题。您正在使用需要填充的密码,因此您要加密的内容的长度必须是16字节的倍数。
您必须填写邮件(例如,在解密后使用您修剪的空格()),或者您需要更改为非填充密码。