Kotlin使用BouncyGPG PGP加密大文件后JSch SFTP传输异常求助
问题排查与修复方案
核心错误原因
- BouncyGPG API使用错误:
BouncyGPG.encryptToStream()....andWriteTo(output)会返回一个经过加密包装的输出流,你必须向这个返回的流写入明文数据,加密逻辑才会生效。你现有代码直接向原始的output写入明文,完全跳过了BouncyGPG的加密处理流程,自然不会生成合法的加密文件。 - 冗余的私钥导入:你配置了
.andDoNotSign(),加密只需要公钥即可,导入私钥属于多余操作,还可能因为密码读取错误引发额外异常。 - 资源泄漏风险:
sendSFTP方法中的JSch会话、SFTP通道使用完没有主动关闭,长期运行会出现连接资源耗尽的问题。 - 语法错误:
sendFileSFTP方法参数MyFile后缺少右括号,encrypt方法的两个参数之间缺少逗号。
修复后的完整代码
BouncyGPG.registerProvider() private val bufferSize: Int = 8 * 1024 fun sendFileSFTP(myFile: MyFile) { PipedInputStream().use { input -> GlobalScope.launch(Dispatchers.IO) { // 显式指定IO调度器,避免阻塞默认线程 PipedOutputStream(input).use { output -> encrypt(myFile, output) } } sendSFTP(input, "mysftp-directory/${myFile.filename}") log.info("Finished Sending $myFile") } } fun encrypt(myFile: MyFile, output: OutputStream) { log.info("ENCRYPTING - $myFile") val pubkey = "/home/myhome/server.gpg" val password = "password" val keyring = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withPassword(password)) // 只导入公钥即可,不需要私钥 keyring.addPublicKey(File(pubkey).readText().toByteArray(charset("US-ASCII"))) // 获取加密包装流,写入明文会自动加密后输出到原始output BouncyGPG.encryptToStream() .withConfig(keyring) .withStrongAlgorithms() .toRecipients("contact@myserver.com") .andDoNotSign() .binaryOutput() .andWriteTo(output).use { encryptOutputStream -> val filepath = Paths.get(myFile.path, myFile.filename) BufferedInputStream(FileInputStream(filepath.toString())).use { input -> input.copyTo(encryptOutputStream, bufferSize) } } log.info("FINISHED ENCRYPTING - $myFile") } fun sendSFTP(inputStream: InputStream, dest: String) { log.info("Sending $dest") var jsch = JSch() jsch.setKnownHosts(properties.knownHosts) var jschSession: Session? = null var channel: ChannelSftp? = null try { jschSession = jsch.getSession(properties.sFTPUsername, properties.sFTPHost, properties.sFTPPort) jschSession.setPassword(properties.sFTPPassword) jschSession.connect() channel = jschSession.openChannel("sftp") as ChannelSftp channel.connect() channel.put(inputStream, dest) } finally { // 主动释放资源 channel?.disconnect() jschSession?.disconnect() } }
额外优化建议
- 避免使用
GlobalScope,建议自行创建限定生命周期的协程作用域,避免任务泄漏。 - 公钥、私钥、密码等敏感配置不要硬编码在代码中,建议放到配置文件或环境变量中读取。
内容的提问来源于stack exchange,提问作者ademarizu
相关产品推荐
相关产品推荐

