Java 错误 : Input length must be multiple of 16 when decrypting with padded cipher

标签 java aes

大家好!我正在尝试制作一个使用 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) {
                }
            }
        }
    }
}

非常感谢您为拯救编程新手提供的所有帮助!

-银

最佳答案

这可能是您见过的最好、最有用的错误消息。它准确地告诉您问题出在哪里。您正在使用需要填充的密码,因此您要加密的内容的长度必须是 16 字节的倍数。

您要么必须填充您的消息(例如,在解密后使用您修剪 () 的空格),要么您需要更改为非填充密码。

关于Java 错误 : Input length must be multiple of 16 when decrypting with padded cipher,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18291987/

相关文章:

Java:for(;;) 与 while(true)

java - Java中 "asynchronous method"是什么意思?

security - 应用手动 AES 加密而不是使用 HTTPS

android - AES 加密 : InvalidKeyException: Key length not 128/192/256 bits

Java - 部分修改文件

java - 如何添加线程 MVC 模式 theView 以避免 JFrame 卡住

Java GUI 不一致

java - Spring Boot 2 安全性 - 预验证 token - 允许健康检查

encryption - 如果只知道 key 和明文,则恢复 AES IV

c# - AES - c# 加密和 objective-c 解密不起作用