2013-08-17 32 views
2

Ello mates!我正在嘗試製作一個使用AES加密的聊天程序,但我遇到了問題。當我嘗試運行該程序時,它會給出您可以在標題中看到的錯誤。我會給你兩個類的代碼,因爲看起來,這是完全診斷問題的唯一方法。這有很多重複,但似乎沒有回答我的具體問題。感謝你的幫助! 客戶:Java錯誤:使用填充密碼解密時,輸入長度必須是16的倍數

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 = "123456789"; 
     // 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) { 
       } 
      } 
     } 
    } 
} 

非常感謝在編程保存完整的小白您的幫助!

- 銀

回答

1

這可能是你永遠不會看到的最好的和最有用的錯誤消息。它告訴你到底是什麼問題。您正在使用需要填充的密碼,以便您正在加密的密碼必須具有16個字節的倍數。

您必須填充消息(例如,在解密後修剪())的空白處),或者您需要更改爲非填充密碼。

+0

謝謝!通過使用少量子字符串和一些新命令,我解決了這個問題。你說得對,這可能是java中最自我解釋的錯誤;) – Silver

相關問題