当前位置: 首页 > 知识库问答 >
问题:

java - 为什么hutool的AES工具类在循环内外表现不一致呢?

武睿
2023-08-08

环境

  • 语言:Kotlin 1.8
  • 环境:Windows 11
  • IDE:Idea 2023.1.3
  • 框架:SpringBoot 3.1.1
  • Hutool版本:cn.hutool.hutool-all:5.8.20
  • KCP Java库:com.github.l42111996.kcp-base:1.6

期望

以KCP作为通讯协议,在客户端和服务端之间使用AES加密通讯数据,在客户端进行加密后的消息,能够在服务端正常解密。

代码(报错版)

客户端:

import cn.hutool.core.codec.Base64import cn.hutool.core.util.HexUtilimport cn.hutool.crypto.Modeimport cn.hutool.crypto.Paddingimport cn.hutool.crypto.SecureUtilimport cn.hutool.crypto.asymmetric.KeyTypeimport cn.hutool.crypto.asymmetric.RSAimport cn.hutool.crypto.symmetric.AESimport cn.hutool.crypto.symmetric.SymmetricAlgorithmimport com.alibaba.fastjson2.JSONimport io.netty.buffer.ByteBufimport io.netty.buffer.ByteBufAllocatorimport kcp.ChannelConfigimport kcp.KcpClientimport kcp.KcpListenerimport kcp.Ukcpimport org.slf4j.LoggerFactoryimport threadPool.disruptor.DisruptorExecutorPoolimport java.net.InetSocketAddressimport java.util.*// 省略了KCP Listener的实现(表现正常)fun main() {    val channelConfig = ChannelConfig()    // 普通模式,参考:https://github.com/skywind3000/kcp#%E5%8D%8F%E8%AE%AE%E9%85%8D%E7%BD%AE    channelConfig.nodelay(false, 40, 0, false)    channelConfig.setiMessageExecutorPool(DisruptorExecutorPool(Runtime.getRuntime().availableProcessors() / 2))    channelConfig.isCrc32Check = true    val kcpClient = KcpClient()    kcpClient.init(channelConfig)    val app = App()    val kcp = kcpClient.connect(InetSocketAddress("127.0.0.1", 9000), channelConfig, app)    val log = LoggerFactory.getLogger("main")    // =================================    // 问题出在下面这句AES的初始化    val aes = AES(Mode.ECB, Padding.ZeroPadding, app.aesKey)    // =================================    while (true) {        val scanner = Scanner(System.`in`)        val nextLine = scanner.nextLine()        val message = aes.encrypt(nextLine.toByteArray(Charsets.UTF_8))        log.info("解密测试:{}", aes.decrypt(message).toString(Charsets.UTF_8))        log.info("发送消息:{},长度:{},Hex:{}", message, message.size, HexUtil.encodeHexStr(message))        kcp?.write(ByteBufAllocator.DEFAULT.buffer().writeBytes(            message        ))    }}

运行上面的代码,会读取命令行输入,然后发送通过AES加密后的数据到服务器,服务器进行解密。

命令行输入:test

服务器输出:

服务器解密后的乱码

这里解密后是一堆乱码,无法正常解密。

代码(正常版)

客户端:

import cn.hutool.core.codec.Base64import cn.hutool.core.util.HexUtilimport cn.hutool.crypto.Modeimport cn.hutool.crypto.Paddingimport cn.hutool.crypto.SecureUtilimport cn.hutool.crypto.asymmetric.KeyTypeimport cn.hutool.crypto.asymmetric.RSAimport cn.hutool.crypto.symmetric.AESimport cn.hutool.crypto.symmetric.SymmetricAlgorithmimport com.alibaba.fastjson2.JSONimport io.netty.buffer.ByteBufimport io.netty.buffer.ByteBufAllocatorimport kcp.ChannelConfigimport kcp.KcpClientimport kcp.KcpListenerimport kcp.Ukcpimport org.slf4j.LoggerFactoryimport threadPool.disruptor.DisruptorExecutorPoolimport java.net.InetSocketAddressimport java.util.*// 省略了KCP Listener的实现(表现正常)fun main() {    val channelConfig = ChannelConfig()    // 普通模式,参考:https://github.com/skywind3000/kcp#%E5%8D%8F%E8%AE%AE%E9%85%8D%E7%BD%AE    channelConfig.nodelay(false, 40, 0, false)    channelConfig.setiMessageExecutorPool(DisruptorExecutorPool(Runtime.getRuntime().availableProcessors() / 2))    channelConfig.isCrc32Check = true    val kcpClient = KcpClient()    kcpClient.init(channelConfig)    val app = App()    val kcp = kcpClient.connect(InetSocketAddress("127.0.0.1", 9000), channelConfig, app)    val log = LoggerFactory.getLogger("main")    while (true) {        val scanner = Scanner(System.`in`)        val nextLine = scanner.nextLine()        // =========================================        // 将AES初始化移到while循环内,就表现正常        val aes = AES(Mode.ECB, Padding.ZeroPadding, app.aesKey)        // =========================================        val message = aes.encrypt(nextLine.toByteArray(Charsets.UTF_8))        log.info("解密测试:{}", aes.decrypt(message).toString(Charsets.UTF_8))        log.info("发送消息:{},长度:{},Hex:{}", message, message.size, HexUtil.encodeHexStr(message))        kcp?.write(ByteBufAllocator.DEFAULT.buffer().writeBytes(            message        ))    }}

同样运行代码,命令行输入:test

服务器输出:

服务器输出正常解密的消息

这里就可以正常解密了。

疑问

非常疑惑的一点在于,刚才的测试都是客户端刚启动,进入循环的第一次命令行读取时进行的,那么此时AES都是只被初始化了一次,对于第一次循环来说,执行的语句是一样的,那么为什么会有不同的结果——在循环外初始化会导致解密错误呢。

希望大佬解答!感谢!

补充

两个版本的代码,在客户端内部发送前的解密测试那里,都是可以正常解密的,但是错误版的代码将消息发送到服务器就会解密错误。

共有1个答案

吴涵育
2023-08-08

在服务器端,对每个接收到的数据块都建一个新的AES实例来解密。或者
在客户端和服务器端都使用一个持久的AES实例,但是要保证你用的是一个可以处理这种情况的加密模式和填充方式。比如,你可以用CBC模式和PKCS5Padding填充方式。

 类似资料:
  • 我试图在任意年份的经济衰退中计算复利。以下promise函数计算可变投资组合的发展。 编辑的清晰度: const是一个从1到20循环一年的数组(是静态的) 三元()在每次运行的不同年份触发,导致不同年份的

  • 所以当我在leetcode上运行这个代码时,它告诉我在列表节点中发现错误循环,但是我一生都不知道为什么。请帮忙,谢谢! }

  • 问题内容: 为什么以下工作正常? 但是据说这是危险的/不正确的: 是否需要在循环外声明变量? 问题答案: 局部变量的范围应始终尽可能小。 在你的例子我相信是不会使用的外while循环,否则你就不会问这个问题,因为它声明的内部while循环不会是一个选项,因为它不会编译。 所以,既然是不使用外循环,在尽可能小的范围是内 while循环。 所以,答案是着重那绝对应该被while循环内声明。没有,没有,

  • 如果我有时运行这些程序,它会在打印“玩家赢”或“玩家输”后继续运行,我可以找到原因。。 这里的输出: 球员滚轴3 2=5 分数是5 玩家滚轮6 4=10 游戏者滚轴6=12 玩家滚轮5 5=10 球员滚轴12=3 球员滚轴1 3=4 球员获胜 球员滚轴4 6=10 球员滚轴4 1=5 构建成功(总时间:0秒)

  • 我正在看freeCodeCamp的一个关于flappy bird游戏的教程https://www.youtube.com/watch?v=pufko5eg8nc 但我正在使用一个类来学习更多关于它的知识。 问题是,图像没有加载到draw()函数的循环中。如果我将drawImage()放在循环之外(像其他图像一样),那么它们就可以工作。 控制台中没有错误,for循环中的两个图像没有出现,我只是不明白

  • 我试图通过将ajax请求中的for循环的值发送到php文件来检查数据库中的值,“each value in request”,然后文件返回变量“avl”,如果不是,则不可用。 问题是,我检查了一个值流,它们都必须返回才能继续我的过程,但条件不会等到for循环结束才进行检查。它在for循环启动之前检查条件,即使代码也不是那样的。例如:在for循环在第50行结束之前,它在第100行执行条件。 它总是通