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

