使用 RSA 进行 Java AES key 交换期间抛出 java.security.InvalidKeyException

标签 java encryption aes rsa public-key-exchange

我正在尝试用 Java 编写一个客户端/服务器程序,该程序允许服务器将使用 AES 加密的消息发送到客户端。现在,我在创建 key 交换协议(protocol)时遇到问题。目前该 key 交换的工作方式是:

  1. 客户端生成 RSA 公钥/私钥对
  2. 客户端将其 RSA 公钥发送到服务器
  3. 服务器生成 AES key 并使用客户端的 RSA 公钥进行加密
  4. 服务器将加密的 AES key 发送给客户端
  5. 双方现在都拥有正确的 AES key ,并且所有消息都可以使用 AES 加密

但是,每次执行到第三步时,我都无法使用客户端的 RSA 公钥对生成的 AES key 进行加密,因为出现以下错误:

java.security.InvalidKeyException: No installed provider supports this key: javax.crypto.spec.SecretKeySpec
    at java.base/javax.crypto.Cipher.chooseProvider(Cipher.java:919)
    at java.base/javax.crypto.Cipher.init(Cipher.java:1275)
    at java.base/javax.crypto.Cipher.init(Cipher.java:1212)
    at test.Server.<init>(Server.java:50)
    at test.Start.main(Start.java:11)

因此,我无法完成我尝试执行的 AES key 交换。

Server.java 用于执行服务器端的操作,而 Client.java 用于执行客户端的所有操作。我的 Server.java 文件如下所示:

public class Server {
    private ServerSocket serverSocket; // Server socket
    private Socket socket; // Socket
    private BufferedReader in; // Reading from stream
    private PrintWriter out; // Writing to stream
    private Key key; // AES key used for encryption

    // Constructor
    public Server() {
        // Initialize the server socket
        try {
            // Setup connections
            serverSocket = new ServerSocket(12345);
            socket = serverSocket.accept();
            out = new PrintWriter(socket.getOutputStream(), true);
            in = new BufferedReader(newInputStreamReader(socket.getInputStream()));

            // Receive the client's public RSA key
            byte[] encodedClientKey = Base64.getDecoder().decode(in.readLine());
            Key clientRSAKey = new SecretKeySpec(encodedClientKey, 0, encodedClientKey.length, "RSA");

            // Generate AES key
            KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES");
            aesKeyGen.init(256);
            key = aesKeyGen.generateKey();

            // Encrypt the AES key using the client's RSA public key
            Cipher c = Cipher.getInstance("RSA");
            c.init(Cipher.ENCRYPT_MODE, clientRSAKey);
            byte[] encryptedAESKey = c.doFinal(key.getEncoded());

            // Send the encrypted AES key to the client
            sendUnencrypted(Base64.getEncoder().encodeToString(encryptedAESKey));
        } catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
            | IllegalBlockSizeException | BadPaddingException e) {
            e.printStackTrace();
        }
    }

    // Receive an unencrypted message
    public String receiveUnencrypted() {
        try {
            // Wait until the stream is ready to be read
            while (true)
                if (in.ready())
                    break;

            return in.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    // Send an unencrypted message
    public void sendUnencrypted(String message) {
        out.println(message);
        out.flush();
    }

    // Send an encrypted message
    public void send(String message) {
        try {
            // Encrypt the message
            Cipher c = Cipher.getInstance("AES");
            c.init(Cipher.ENCRYPT_MODE, key);
            String encoded = Base64.getEncoder().encodeToString(message.getBytes("utf-8"));
            byte[] encrypted = c.doFinal(encoded.getBytes());
            String encryptedString = Base64.getEncoder().encodeToString(encrypted);

            // Send the encrypted message
            out.println(encryptedString);
            out.flush();
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
            | BadPaddingException | UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

我的 Client.java 文件如下所示:

public class Client {
    private Socket socket; // Socket
    private BufferedReader in; // Reading from stream
    private PrintWriter out; // Writing to stream
    private Key key; // AES key

    // Constructor
    public Client() {
        try {
            // Create streams to server
            socket = new Socket("127.0.0.1", 12345);
            out = new PrintWriter(socket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            // Generate an RSA key pair
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(2048);
            KeyPair kp = keyGen.generateKeyPair();

            // Send out our public key to the server
            byte[] publicKey = kp.getPublic().getEncoded();
            sendUnencrypted(Base64.getEncoder().encodeToString(publicKey));

            // Recieve and decrypt the AES key sent from the server
            String encryptedKey = receiveUnencrypted();
            Cipher c = Cipher.getInstance("RSA");
            c.init(Cipher.DECRYPT_MODE, kp.getPrivate());
            byte[] AESKey = c.doFinal(encryptedKey.getBytes());
            key = new SecretKeySpec(AESKey, 0, AESKey.length, "AES");
        } catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                | IllegalBlockSizeException | BadPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    // Receive an unencrypted message
    public String receiveUnencrypted() {
        try {
            // Wait until the stream is ready to be read
            while (true)
                if (in.ready())
                    break;

            return in.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    // Send an unencrypted message
    public void sendUnencrypted(String message) {
        out.println(message);
        out.flush();
    }

    // Receive an encrypted message
    public String receive() {
        try {
            // Wait until the stream is ready to be read
            while (true)
                if (in.ready())
                    break;

            // Obtain the encrypted message
            String encrypted = in.readLine();

            // Decrypt and return the message
            Cipher c = Cipher.getInstance("AES");
            c.init(Cipher.DECRYPT_MODE, key);
            byte[] decoded = Base64.getDecoder().decode(encrypted);
            String utf8 = new String(c.doFinal(decoded));
            String plaintext = new String(Base64.getDecoder().decode(utf8));

            // Return the message
            return plaintext;
        } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
                | IllegalBlockSizeException | BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Start.java 用于初始化服务器和客户端。

package test;

import java.util.Scanner;

public class Start {
    public static void main(String args[]) {
        Scanner scan = new Scanner(System.in);
        System.out.println("1.) Create data server.\n2.) Connect to data server.\nPlease select an option: ");
        int option = scan.nextInt();
        if (option == 1) {  // Setup a server if they choose option one
            Server s = new Server();
            s.send("Hello");
        } else if (option == 2) {  // Setup a client if they choose option two
            Client c = new Client();
            System.out.println(c.receive());
        }

        // Close scanner
        scan.close();
    }
}

最佳答案

首先,您不能使用 SecretKeySpec 来恢复 RSA 公钥。在 Server 的构造函数中,更改

Key clientRSAKey = new SecretKeySpec(encodedClientKey, 0, encodedClientKey.length, "RSA");

Key clientRSAKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(encodedClientKey));

其次,您需要解码base64编码的加密 key 。在您的 Client 构造函数中,更改

String encryptedKey = receiveUnencrypted();

byte[] encryptedKey = Base64.getDecoder().decode(receiveUnencrypted());

最后,在您的 Client 构造函数中,更改

byte[] AESKey = c.doFinal(encryptedKey.getBytes());

byte[] AESKey = c.doFinal(encryptedKey);

关于使用 RSA 进行 Java AES key 交换期间抛出 java.security.InvalidKeyException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51598024/

相关文章:

java - Android - ViewFlipper 问题

java - 如何使用 docker 运行 shell 脚本和 java 应用程序?

sql-server - 是否可以通过明文类型确定 ENCRYPTBYKEY 最大返回值?

php - 在客户端使用 openssl_encrypt 加密用户名和密码?

ios - 在 iOS 上加密 SQLite 数据库文件

java - Spring 应用程序上下文加密

java - 在解密过程中,如何将GCM身份验证标签放在密码流的末尾需要内部缓冲?

java - 如何编写在二叉树(java)中按级别顺序(从左到右)插入节点的方法?

java - 从 WSDL 生成的自顶向下 Java 服务中的 CXF 3.0.3 部署错误

c++ - AES CBC 加密/解密只解密前 16 个字节