docker镜像出错问题-2-复现
先说结论:
- Alpine 镜像 + LANG=en_US.UTF-8 验签成功
- Debian 镜像 + LANG=en_US.UTF-8 验签失败
- Debian 镜像 + LANG=C.UTF-8 验签成功
使用下面的 dockerfile构建后运行就正常。
FROM openjdk:8-jdk-alpine
ENV LANG="en_US.UTF-8"
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /app
COPY /target/user-app.jar /app/user-app.jar
EXPOSE 8003
ENTRYPOINT ["java","-jar","/app/user-app.jar"]把上面的 openjdk:8-jdk-alpine 改为 openjdk:8u322-jdk-bullseye,构建后运行就会验签失败。
而使用下面的
FROM openjdk:8u322-jdk-bullseye
ENV LANG="C.UTF-8"
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /app
COPY /target/user-app.jar /app/user-app.jar
EXPOSE 8003
ENTRYPOINT ["java","-jar","/app/user-app.jar"]就正常。
核心结论(最关键)
内容由ai提供
Alpine = musl libc
- → 不支持 en_US.UTF-8 → 实际 fallback 到 C.UTF-8
- → Java 在 musl 上永远用 C.UTF-8
- → 验签时的 canonical string/stable hashing 不受影响
- → 验签成功
Debian = glibc
- → 完整 locale 支持
- → 设置 LANG=en_US.UTF-8 就真的启用 en_US.UTF-8
- → en_US.UTF-8 会影响:
- Collator
- Normalizer
- 文件名排序
- Unicode canonical form implementation
- JAR Manifest digest 计算时的编码处理
- → 导致某些签名文件(尤其是国产 SM 系列、PKCS7、BouncyCastle 生成的签名)hash 值不一致 → 验签失败
而当强制:
ENV LANG=C.UTF-8Java 重新回到最朴素的 UTF-8 C 语义 → 不使用 locale 特性 → digest 计算稳定 → 验签恢复正常
关键问题:为什么 locale 会影响 Java 验签?
因为 Jar 验签流程中,JDK 会对 Manifest 中的属性和 entry 进行:
- 排序
- 去空格
- 规范化
- 按照 ASCII 对 key 排序
- 按 UTF-8 写入 canonical form
在 glibc + en_US.UTF-8 下:
- 字符分类(upper/lower)
- 排序规则(collation)
- whitespace 判断
- Unicode normalize(NFC/NFKC)
都会比 C locale 多做额外处理。
这种变化足以导致 SHA-256 digest 不一致,从而验签失败。
最终结论
- Alpine 默认不支持 en_US.UTF-8,但这不是问题。因为它 fallback 到了 C.UTF-8,使得 Java 验签反而正常
- Debian 支持 en_US.UTF-8,反而会导致 Java 验签失败。因为 glibc locale 会改变 Java 的字符串处理行为。强制 C.UTF-8 让两者行为一致 → 验签成功。
- java代码中把字符串转为字节数据时要使用具体编码。
str.getBytes(StandardCharsets.UTF_8); // 建议 str.getBytes(); // 不建议,在不同容器里执行结果不稳定。
示例工程如下
springboot2.2.10-http-sign.7z作者:张三 创建时间:2025-11-28 10:03
最后编辑:张三 更新时间:2025-11-30 20:58
最后编辑:张三 更新时间:2025-11-30 20:58