Java文件解密遇阻:数字签名未验证+CipherInputStream无输出求助
解决你的文件加密解密程序问题:签名验证失败+解密输出为空
嘿,我看了你的代码和问题描述,发现了几个关键的问题点,帮你一一梳理并给出修复方案:
一、数字签名的逻辑完全搞反了!
你提到“用发送方私钥和接收方公钥生成数字签名”——这是错误的思路哦。数字签名的核心逻辑是:
- 发送方(Alice):用自己的私钥对要确认的内容(比如原始数据哈希、对称密钥+IV组合)签名,证明“这确实是我发的”
- 接收方(Bob):用发送方的公钥验证签名,确认内容没被篡改且来自合法发送方
你现在的代码里试图用Bob的公钥参与签名生成,这完全不符合签名的设计逻辑,肯定会导致验证失败。
正确的签名生成(Alice加密时)
// 我们选择对对称密钥+IV的组合签名(确保密钥和IV没被篡改) byte[] contentToSign = ByteBuffer.allocate(symmetricKey.getEncoded().length + ivByte.length) .put(symmetricKey.getEncoded()) .put(ivByte) .array(); // 初始化签名器,用Alice的私钥 Signature signature = Signature.getInstance("SHA256withRSA"); // 推荐用SHA256+RSA,更安全 signature.initSign((PrivateKey) aliceKey); signature.update(contentToSign); byte[] signBytes = signature.sign(); // 把签名转成Base64字符串,方便存入配置文件传输 String signatureBase64 = Base64.getEncoder().encodeToString(signBytes);
正确的签名验证(Bob解密时)
// 从配置文件读取签名、加密后的密钥、IV Properties config = new Properties(); config.load(new FileInputStream("你的配置文件路径")); String signatureBase64 = config.getProperty("signature"); String encryptedKeyBase64 = config.getProperty("encryptedKey"); String ivBase64 = config.getProperty("iv"); // 从Bob的密钥库获取Alice的公钥(Bob的密钥库必须导入过Alice的证书) Certificate aliceCert = bobKeystore.getCertificate("alicecert"); // 确保别名正确 PublicKey alicePubKey = aliceCert.getPublicKey(); // 初始化验证器,用Alice的公钥 Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(alicePubKey); // 重构签名时的内容(必须和签名时完全一致) byte[] encryptedKeyBytes = Base64.getDecoder().decode(encryptedKeyBase64); byte[] ivBytes = Base64.getDecoder().decode(ivBase64); byte[] contentToVerify = ByteBuffer.allocate(encryptedKeyBytes.length + ivBytes.length) .put(encryptedKeyBytes) .put(ivBytes) .array(); signature.update(contentToVerify); boolean isSignValid = signature.verify(Base64.getDecoder().decode(signatureBase64)); if (!isSignValid) { throw new SecurityException("数字签名验证失败!数据可能被篡改或来源非法"); }
二、CipherInputStream使用错误+IV处理不当导致输出为空
你的加密代码里有个致命错误:把IV字节数组直接转成String存储——String ivString = new String(curIV);。IV是随机生成的字节,直接用默认字符编码转换会丢失字节信息,导致解密时无法还原正确的IV,自然解不出内容。
另外,解密时的CipherInputStream使用也需要注意正确的流处理方式:
1. 修复IV和密钥的存储方式
加密时必须用Base64编码字节数组,确保字节完整:
// 加密后存储IV和加密后的对称密钥 String ivBase64 = Base64.getEncoder().encodeToString(cipher.getIV()); String encryptedKeyBase64 = Base64.getEncoder().encodeToString(keyCipher.doFinal(symmetricKey.getEncoded())); // 把这些字符串写入配置文件
2. 正确的解密流实现
这里给你补全正确的decrypt函数:
public static void decrypt(String storePass) throws Exception { // 1. 加载配置参数 Properties config = new Properties(); config.load(new FileInputStream("C:\\Users\\Victoria\\Desktop\\config.properties")); String ivBase64 = config.getProperty("iv"); String encryptedKeyBase64 = config.getProperty("encryptedKey"); String signatureBase64 = config.getProperty("signature"); // 2. 先验证数字签名(参考上面的签名验证代码) // ... 这里插入签名验证逻辑 ... // 3. 从Bob的密钥库获取私钥,解密对称密钥 char[] bobStorePassArr = storePass.toCharArray(); char[] bobKeyPassArr = "bobKey".toCharArray(); // 确保和Bob密钥的密码一致 FileInputStream bobKsStream = new FileInputStream("C:\\Users\\Victoria\\Desktop\\cryptography\\bobKeystore.jks"); KeyStore bobKeystore = KeyStore.getInstance("JKS"); bobKeystore.load(bobKsStream, bobStorePassArr); PrivateKey bobPrivateKey = (PrivateKey) bobKeystore.getKey("bob", bobKeyPassArr); // 解密对称密钥 Cipher keyDecryptCipher = Cipher.getInstance("RSA"); keyDecryptCipher.init(Cipher.DECRYPT_MODE, bobPrivateKey); byte[] symmetricKeyBytes = keyDecryptCipher.doFinal(Base64.getDecoder().decode(encryptedKeyBase64)); SecretKey symmetricKey = new SecretKeySpec(symmetricKeyBytes, "AES"); // 还原IV IvParameterSpec iv = new IvParameterSpec(Base64.getDecoder().decode(ivBase64)); // 4. 初始化文件解密Cipher Cipher fileDecryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); fileDecryptCipher.init(Cipher.DECRYPT_MODE, symmetricKey, iv); // 5. 使用CipherInputStream解密文件,用try-with-resources自动关流 File encryptedFile = new File("C:\\Users\\Victoria\\Desktop\\encryptedData.txt"); File decryptedFile = new File("C:\\Users\\Victoria\\Desktop\\decryptedData.txt"); try (FileInputStream fis = new FileInputStream(encryptedFile); CipherInputStream cis = new CipherInputStream(fis, fileDecryptCipher); FileOutputStream fos = new FileOutputStream(decryptedFile)) { byte[] buffer = new byte[4096]; // 用4KB缓冲区,提升效率 int bytesRead; // 循环读取直到流结束,不能只读一次! while ((bytesRead = cis.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } fos.flush(); // 确保所有数据写入文件 } }
三、其他需要注意的细节
- 代码完整性:你提供的加密代码最后一行
keyCipher.init(Ciphe不完整,应该补成keyCipher.init(Cipher.ENCRYPT_MODE, bobPublicKey);,用来用Bob的公钥加密对称密钥。 - 异常处理:你的代码几乎没有异常捕获,建议添加
try-catch块打印详细异常信息,这样Eclipse就能显示错误,方便你排查问题。 - 密钥库别名:确保Bob的密钥库中确实导入了Alice的证书,且别名正确,否则无法获取Alice的公钥验证签名。
内容的提问来源于stack exchange,提问作者Loly
相关产品推荐
相关产品推荐

