Issue: SM3 摘要计算在特殊情况下会报错
问题描述
SinopecSmCryptoServiceImpl 的 SM3 摘要计算方法在以下特殊情况下会抛出异常或返回错误结果:
- 处理二进制文件(图片、PDF等)时,
sm3Hash(InputStream) 方法会失败
- SDK 返回非预期格式时,
sm3Hash(String) 方法可能返回错误结果
- 输入数据为空或 null 时,缺少明确的参数校验
影响范围
- 受影响方法:
SmCryptoService.sm3Hash(String data)
SmCryptoService.sm3Hash(InputStream inputStream)
SmCryptoService.sm3HashFile(File file)
- 受影响实现:
SinopecSmCryptoServiceImpl
- 影响场景:
- 计算二进制文件的 SM3 摘要(图片、PDF、压缩包等)
- SDK 返回非 JSONObject 格式的结果
- 传入 null 或空字符串
- 处理大文件时可能出现内存问题
问题原因
问题1:二进制文件处理错误
在 SinopecSmCryptoServiceImpl.java 第 204-218 行,sm3Hash(InputStream) 方法存在缺陷:
public String sm3Hash(InputStream inputStream) {
try {
// 读取流数据
byte[] buffer = new byte[8192];
StringBuilder data = new StringBuilder();
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
data.append(new String(buffer, 0, bytesRead, StandardCharsets.UTF_8)); // ❌ 问题所在
}
return sm3Hash(data.toString());
} catch (Exception e) {
log.error("SM3流摘要计算失败", e);
throw new RuntimeException("SM3流摘要计算失败: " + e.getMessage(), e);
}
}
问题2:SDK 返回结果处理不完善
在 SinopecSmCryptoServiceImpl.java 第 166-186 行,sm3Hash(String) 方法:
Object result = sdkAdapter.callSDKMethod(...);
if (result instanceof JSONObject) {
return ((JSONObject) result).getString("digest");
}
return result.toString(); // ❌ 如果 result 为 null 或其他类型,可能返回错误结果
问题分析:
-
UTF-8 解码错误:将二进制数据强制用 UTF-8 解码成字符串,对于非文本文件会导致:
- 无效的 UTF-8 序列被替换为替换字符(U+FFFD),改变原始数据
- 某些字节序列无法解码,可能抛出
CharacterCodingException
- 即使不抛异常,解码后的字符串与原始字节数据不一致,导致摘要值错误
-
内存问题:对于大文件,将所有数据读入 StringBuilder 可能导致内存溢出(OOM)
-
结果处理不完善:当 SDK 返回 null 或非预期类型时,直接调用 toString() 可能返回 "null" 或其他错误值
-
缺少参数校验:未对输入参数进行 null 或空值检查
复现步骤
场景1:二进制文件处理失败
- 准备一个二进制文件(如
test.jpg 或 test.pdf)
- 调用
smCryptoService.sm3HashFile("test.jpg") 或 smCryptoService.sm3Hash(new FileInputStream(new File("test.jpg")))
- 观察结果:
- 可能抛出
CharacterCodingException 或 RuntimeException
- 或返回错误的摘要值(与正确实现不一致)
场景2:SDK 返回非预期格式
- Mock SDK 返回 null 或非 JSONObject 类型
- 调用
smCryptoService.sm3Hash("测试数据")
- 观察结果:可能返回 "null" 字符串或其他错误值
场景3:空值处理
- 调用
smCryptoService.sm3Hash(null) 或 smCryptoService.sm3Hash("")
- 观察结果:可能抛出
NullPointerException 或返回错误结果
预期行为
- 二进制文件:应该直接对字节流进行摘要计算,不经过字符串转换
- SDK 返回结果:应该正确处理各种返回类型,对 null 或非预期类型抛出明确的异常
- 参数校验:应该在方法开始处检查参数,对 null 或空值抛出
IllegalArgumentException
建议修复方案
方案一:修复二进制文件处理(推荐)
@Override
public String sm3Hash(InputStream inputStream) {
try {
// 读取流数据为字节数组
byte[] buffer = new byte[8192];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
byte[] dataBytes = baos.toByteArray();
// 将字节数组转换为Base64字符串(保持数据完整性)
String dataBase64 = Base64.getEncoder().encodeToString(dataBytes);
// 调用SDK接口
Object result = sdkAdapter.callSDKMethod(
"getDigest",
sdkAdapter.getSed(),
sdkAdapter.getLocalProxyUrl(),
dataBase64, // Base64编码的字符串
1 // Hash算法ID(1=SM3)
);
return extractDigest(result);
} catch (Exception e) {
log.error("SM3流摘要计算失败", e);
throw new RuntimeException("SM3流摘要计算失败: " + e.getMessage(), e);
}
}
方案二:完善结果处理和参数校验
@Override
public String sm3Hash(String data) {
try {
// 参数校验
if (data == null) {
throw new IllegalArgumentException("SM3摘要计算的输入数据不能为null");
}
Object result = sdkAdapter.callSDKMethod(
"getDigest",
sdkAdapter.getSed(),
sdkAdapter.getLocalProxyUrl(),
data,
1
);
return extractDigest(result);
} catch (Exception e) {
log.error("SM3摘要计算失败", e);
throw new RuntimeException("SM3摘要计算失败: " + e.getMessage(), e);
}
}
private String extractDigest(Object result) {
if (result == null) {
throw new RuntimeException("SDK返回结果为空");
}
if (result instanceof JSONObject) {
String digest = ((JSONObject) result).getString("digest");
if (digest == null) {
throw new RuntimeException("SDK返回的digest字段为空");
}
return digest;
}
// 如果返回的是字符串,直接返回
if (result instanceof String) {
return (String) result;
}
throw new RuntimeException("SDK返回了非预期的结果类型: " + result.getClass().getName());
}
相关代码位置
- 问题代码1:
src/main/java/com/github/luxl/smcrypto/service/impl/SinopecSmCryptoServiceImpl.java 第 204-218 行(InputStream 处理)
- 问题代码2:
src/main/java/com/github/luxl/smcrypto/service/impl/SinopecSmCryptoServiceImpl.java 第 166-186 行(结果处理)
- 参考实现:
src/main/java/com/github/luxl/smcrypto/service/impl/DefaultSmCryptoServiceImpl.java 第 169-186 行(正确的流处理方式)
优先级
高 - 影响二进制文件的 SM3 摘要计算功能,可能导致数据完整性校验失败,且错误处理不完善会影响问题排查。
标签
bug
sm3
inputstream
binary-file
error-handling
sinopec-implementation
Issue: SM3 摘要计算在特殊情况下会报错
问题描述
SinopecSmCryptoServiceImpl的 SM3 摘要计算方法在以下特殊情况下会抛出异常或返回错误结果:sm3Hash(InputStream)方法会失败sm3Hash(String)方法可能返回错误结果影响范围
SmCryptoService.sm3Hash(String data)SmCryptoService.sm3Hash(InputStream inputStream)SmCryptoService.sm3HashFile(File file)SinopecSmCryptoServiceImpl问题原因
问题1:二进制文件处理错误
在
SinopecSmCryptoServiceImpl.java第 204-218 行,sm3Hash(InputStream)方法存在缺陷:问题2:SDK 返回结果处理不完善
在
SinopecSmCryptoServiceImpl.java第 166-186 行,sm3Hash(String)方法:问题分析:
UTF-8 解码错误:将二进制数据强制用 UTF-8 解码成字符串,对于非文本文件会导致:
CharacterCodingException内存问题:对于大文件,将所有数据读入
StringBuilder可能导致内存溢出(OOM)结果处理不完善:当 SDK 返回 null 或非预期类型时,直接调用
toString()可能返回 "null" 或其他错误值缺少参数校验:未对输入参数进行 null 或空值检查
复现步骤
场景1:二进制文件处理失败
test.jpg或test.pdf)smCryptoService.sm3HashFile("test.jpg")或smCryptoService.sm3Hash(new FileInputStream(new File("test.jpg")))CharacterCodingException或RuntimeException场景2:SDK 返回非预期格式
smCryptoService.sm3Hash("测试数据")场景3:空值处理
smCryptoService.sm3Hash(null)或smCryptoService.sm3Hash("")NullPointerException或返回错误结果预期行为
IllegalArgumentException建议修复方案
方案一:修复二进制文件处理(推荐)
方案二:完善结果处理和参数校验
相关代码位置
src/main/java/com/github/luxl/smcrypto/service/impl/SinopecSmCryptoServiceImpl.java第 204-218 行(InputStream 处理)src/main/java/com/github/luxl/smcrypto/service/impl/SinopecSmCryptoServiceImpl.java第 166-186 行(结果处理)src/main/java/com/github/luxl/smcrypto/service/impl/DefaultSmCryptoServiceImpl.java第 169-186 行(正确的流处理方式)优先级
高 - 影响二进制文件的 SM3 摘要计算功能,可能导致数据完整性校验失败,且错误处理不完善会影响问题排查。
标签
bugsm3inputstreambinary-fileerror-handlingsinopec-implementation