2

サーバーがAESを使用して暗号化されたメッセージをクライアントに送信できるようにするクライアント/サーバープログラムをJavaで作成しようとしています。現在、鍵交換プロトコルの作成中に問題が発生しています。この鍵交換の現在の仕組みは次のとおりです。

  1. クライアントが RSA 公開鍵と秘密鍵のペアを生成
  2. クライアントは自分の RSA 公開鍵をサーバーに送信します
  3. サーバーは、クライアントの RSA 公開鍵を使用して AES キーを生成および暗号化します。
  4. サーバーは暗号化された AES キーをクライアントに送信します
  5. 双方が正しい AES キーを持ち、AES を使用してすべてのメッセージを暗号化できるようになりました

ただし、ステップ 3 に進むたびに、次のエラーが発生するため、生成された AES キーをクライアントの RSA 公開キーで暗号化できません。

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 鍵交換を完了できません。

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();
    }
}
4

1 に答える 1

2

SecretKeySpecまず、 RSA 公開鍵の復元には使用できません。Serverのコンストラクターで、変更します

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

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

次に、base64 でエンコードされた暗号化キーをデコードする必要があります。Clientコンストラクターで、変更します

String encryptedKey = receiveUnencrypted();

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

最後に、Clientコンストラクターで、変更します

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

byte[] AESKey = c.doFinal(encryptedKey);
于 2018-07-30T16:30:55.110 に答える