You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX点击按钮用私钥加密文件报错及解决方法

问题修复:JavaFX文件选择+RSA私钥加密报错

我是JavaFX和密码学新手,项目需求是通过文件选择器选择文件,并用私钥加密。设置了两个按钮:chooseFileBtn和sendFileBtn,选完文件后点击sendFileBtn执行加密操作,但代码报错,相关代码和错误信息如下:

Controller代码

package com.example.demo1;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.stage.FileChooser;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

public class HelloController {
    @FXML
    private Label chooseFileBtn;

    @FXML
    private Label sendFileBtn;

    @FXML
    private ListView listView;

    File selectedFile;

    @FXML
    public void chooseFileButtonClick(ActionEvent event) throws NoSuchAlgorithmException, IOException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        FileChooser fc = new FileChooser();
        File selectedFile = fc.showOpenDialog(null);

        if( selectedFile != null)
        {
            listView.getItems().add(selectedFile.getPath());
        }
        else {
            System.out.println("File is now valid");
        }
    }

    @FXML
    public void sendFileButtonClick(ActionEvent event)throws Exception{

        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(2048);
        KeyPair pair = generator.generateKeyPair();
        PrivateKey privateKey = pair.getPrivate();
        PublicKey publicKey = pair.getPublic();

        try (FileOutputStream fos = new FileOutputStream("private.key")) {
            fos.write(privateKey.getEncoded());
        }
        File privateKeyFile = new File("private.key");
        byte[] privateKeyBytes = Files.readAllBytes(privateKeyFile.toPath());
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        EncodedKeySpec privateKeySpec = new X509EncodedKeySpec(privateKeyBytes);
        keyFactory.generatePublic(privateKeySpec);

        byte[] fileBytes = Files.readAllBytes(selectedFile.toPath());

        Cipher encryptCipher = Cipher.getInstance("RSA");
        encryptCipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes);

        try (FileOutputStream stream = new FileOutputStream(selectedFile)) {
            stream.write(encryptedFileBytes);
        }
    }
}

FXML代码

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<VBox alignment="CENTER" prefHeight="327.0" prefWidth="323.0" spacing="20.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demo1.HelloController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
    </padding>
   <children>
      <Label prefHeight="127.0" prefWidth="276.0" text="File Transfer Application">
         <font>
            <Font name="American Typewriter" size="23.0" />
         </font>
      </Label>
      <Button fx:id="chooseFileBtn1" mnemonicParsing="false" onAction="#chooseFileButtonClick" text="Choose A File" />
      <ListView fx:id="listView" prefHeight="28.0" prefWidth="283.0" />
      <Button fx:id="sendFileBtn1" mnemonicParsing="false" onAction="#sendFileButtonClick" text="Send The File" />
   </children>
    
</VBox>

报错信息

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
Caused by: java.lang.reflect.InvocationTargetException
Caused by: java.security.spec.InvalidKeySpecException
Caused by: java.security.InvalidKeyException

错误原因分析

  1. 私钥解析逻辑错误:使用X509EncodedKeySpec解析私钥字节,该类仅适用于解析公钥,私钥需用PKCS8EncodedKeySpec。且这段生成私钥后写入再读取的操作完全多余,直接使用生成的PrivateKey对象即可。
  2. 变量作用域问题:chooseFileButtonClick方法中定义了局部变量selectedFile,未赋值给类成员变量selectedFile,导致sendFileButtonClick执行时selectedFile为null,触发空指针异常。
  3. FXML与Controller绑定不匹配:FXML中按钮的fx:id为chooseFileBtn1、sendFileBtn1,但Controller中变量名是chooseFileBtn、sendFileBtn,且类型错误地定义为Label(实际是Button),会导致控件注入失败。
  4. RSA加密局限性:2048位RSA最多只能加密245字节的数据,直接加密大文件会抛出IllegalBlockSizeException,正确做法是用对称加密(如AES)加密文件,再用RSA加密对称密钥。
  5. 拼写逻辑错误:System.out.println("File is now valid");应为File is not valid,逻辑完全相反。

修复方案及代码

修正后的Controller代码

package com.example.demo1;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.stage.FileChooser;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;

public class HelloController {
    @FXML
    private Button chooseFileBtn1;

    @FXML
    private Button sendFileBtn1;

    @FXML
    private ListView<String> listView;

    private File selectedFile;

    @FXML
    public void chooseFileButtonClick(ActionEvent event) {
        FileChooser fc = new FileChooser();
        selectedFile = fc.showOpenDialog(null);

        if (selectedFile != null) {
            listView.getItems().add(selectedFile.getPath());
        } else {
            System.out.println("File is not valid");
        }
    }

    @FXML
    public void sendFileButtonClick(ActionEvent event) throws Exception {
        // 生成RSA密钥对
        KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA");
        rsaGenerator.initialize(2048);
        KeyPair rsaPair = rsaGenerator.generateKeyPair();
        PrivateKey privateKey = rsaPair.getPrivate();

        // 生成AES对称密钥(用于加密大文件)
        KeyGenerator aesGenerator = KeyGenerator.getInstance("AES");
        aesGenerator.initialize(256);
        SecretKey aesKey = aesGenerator.generateKey();

        // 用AES加密文件
        Cipher aesCipher = Cipher.getInstance("AES");
        aesCipher.init(Cipher.ENCRYPT_MODE, aesKey);
        byte[] fileBytes = Files.readAllBytes(selectedFile.toPath());
        byte[] encryptedFileBytes = aesCipher.doFinal(fileBytes);

        // 用RSA私钥加密AES密钥
        Cipher rsaCipher = Cipher.getInstance("RSA");
        rsaCipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] encryptedAesKey = rsaCipher.doFinal(aesKey.getEncoded());

        // 保存加密后的文件和加密的AES密钥(避免覆盖原文件)
        try (FileOutputStream fileOut = new FileOutputStream(selectedFile.getAbsolutePath() + ".encrypted")) {
            fileOut.write(encryptedFileBytes);
        }

        try (FileOutputStream keyOut = new FileOutputStream(selectedFile.getAbsolutePath() + ".aeskey.encrypted")) {
            keyOut.write(encryptedAesKey);
        }

        System.out.println("文件加密完成,已生成加密文件和加密的AES密钥");
    }
}

修正后的FXML代码

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<VBox alignment="CENTER" prefHeight="327.0" prefWidth="323.0" spacing="20.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demo1.HelloController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
    </padding>
   <children>
      <Label prefHeight="127.0" prefWidth="276.0" text="File Transfer Application">
         <font>
            <Font name="American Typewriter" size="23.0" />
         </font>
      </Label>
      <Button fx:id="chooseFileBtn1" mnemonicParsing="false" onAction="#chooseFileButtonClick" text="Choose A File" />
      <ListView fx:id="listView" prefHeight="28.0" prefWidth="283.0" />
      <Button fx:id="sendFileBtn1" mnemonicParsing="false" onAction="#sendFileButtonClick" text="Send The File" />
   </children>
    
</VBox>

内容的提问来源于stack exchange,提问作者darkman106

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 13:25:33