# Keystore was tampered with, or password was incorrect 怎么修

> Java 无法通过 JKS 密钥库的完整性校验：多数是库密码不对，也可能是文件被 Maven 资源过滤或错误的传输方式损坏了。

Source: https://ssl.sh/zh/errors/java-keystore-tampered-or-password-incorrect/
Last updated: 2026-11-10

**`java.io.IOException: Keystore was tampered with, or password was incorrect` 的意思是：Java 打开了一个 JKS 密钥库，用你给的密码计算完整性校验值，结果和文件里存的不一致。** 大多数情况就是库密码填错了；其余情况多半是文件在部署过程中被损坏，最常见的是 Maven 资源过滤或者按文本方式复制。文件被篡改的情况很少。

## 这条报错是什么意思

这条报错表示 JKS 文件的完整性摘要对不上：文件末尾存着一段用文件内容和库密码计算出的摘要。用非空密码加载密钥库时，JDK 会重新计算这段摘要，不一致就由 `JavaKeyStore` 抛出这条 `IOException`，原因（cause）是 `UnrecoverableKeyException: Password verification failed`。JDK 无法区分“密码错了”和“文件被改了”，所以报错里两个都提。

通常你会在 keytool 或服务器日志里看到：

```text
keytool error: java.io.IOException: Keystore was tampered with, or password was incorrect
Caused by: java.security.UnrecoverableKeyException: Password verification failed
```

JDK 9 起默认密钥库类型是 PKCS12，而且 `keystore.type.compat=true` 让 JKS 和 PKCS12 两种类型都能加载对方格式的文件。PKCS12 文件密码错误时报的是另一条：`keystore password was incorrect`。下面的修法对两者都适用。

排查从一步开始：在服务器上用 keytool、同一个文件、同一个密码打开一次。打得开，问题在应用拿到的密码或路径；打不开，再对比文件校验和，判断是密码错了还是文件坏了。不要一上来就重新生成证书，那样会掩盖配置错误，下次部署还会再出现。

## 常见原因（按出现频率排序）

这个报错有 5 种原因，最常见的是库密码不对，包括密码在传给 Java 之前被 shell 或 YAML 改掉。

### 1. 库密码不对

打错了、用了另一个环境的密码、自建的库却用了默认的 `changeit`，或者密码在传给 Java 之前被改掉了：`$` 被 shell 展开、`#` 在 properties 文件里被当成注释、YAML 里没加引号。

- **确认：** 用完全相同的文件和密码测试，密码加引号：

```bash
keytool -list -keystore /opt/app/keystore.jks -storepass 'the-password'
```

keytool 能打开，说明文件没问题，是应用拿到的密码或文件不一样。

- **修复：** 在应用实际读取的地方改正（`server.ssl.key-store-password`、Tomcat 的 `certificateKeystorePassword`、`javax.net.ssl.trustStorePassword` 等），并在 shell 和 YAML 里给它加引号。

### 2. 密钥库和信任库的配置搞混了

`javax.net.ssl.trustStore` 指向了服务器的密钥库，或者自建信任库用别的密码创建，却配置了 `trustStorePassword=changeit`。

- **确认：** 看进程实际使用的 JVM 参数：`ps -ef | grep java`，或 `jcmd <pid> VM.system_properties | grep javax.net.ssl`。
- **修复：** 每个属性指向正确的文件，并配上它自己的密码。

### 3. 文件在传输或构建中被损坏

二进制文件被当成文本处理就会损坏。Maven 文档明确警告：过滤含二进制内容的文件很可能把它弄坏。Git 换行符转换、FTP 用 ASCII 模式传输、提交的是 Git LFS 指针而不是文件本身、Kubernetes Secret 被 base64 编码了两次，表现都一样。

- **确认：** 对比原始文件和部署后文件的校验和，再看文件头：

```bash
sha256sum keystore.jks /opt/app/keystore.jks
xxd -l 4 /opt/app/keystore.jks     # JKS 文件以 feed feed 开头
```

- **修复：** 不要过滤密钥库文件，在 Git 里标记为二进制，用二进制方式复制：

```xml
<plugin>
  <artifactId>maven-resources-plugin</artifactId>
  <configuration>
    <nonFilteredFileExtensions>
      <nonFilteredFileExtension>jks</nonFilteredFileExtension>
      <nonFilteredFileExtension>p12</nonFilteredFileExtension>
    </nonFilteredFileExtensions>
  </configuration>
</plugin>
```

```text
# .gitattributes
*.jks binary
*.p12 binary
```

### 4. 应用加载的不是你以为的那个文件

相对路径在另一个工作目录下解析、JAR 包里打进了同名密钥库，都可能让你改的文件没被用上。改用绝对路径，并在启动日志里打印实际解析出的路径。

### 5. 文件确实被改过

很少见，但如果校验和不一致又说不清原因，应该按安全事件处理：更换私钥并重新签发证书。

## 分平台修复命令

各平台的修法都围绕 keytool：用应用的同一个密码验证文件、修改密码，或者转换成 PKCS12。

**Linux 和 macOS：**

```bash
keytool -list -v -keystore /opt/app/keystore.jks -storepass 'the-password' | head -20
keytool -storepasswd -keystore /opt/app/keystore.jks          # 修改密码（需要旧密码）
keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.p12 -deststoretype pkcs12
```

**Windows（PowerShell）：** 用单引号，避免 `$` 被展开：

```powershell
& "$env:JAVA_HOME\bin\keytool.exe" -list -keystore C:\app\keystore.jks -storepass 'the-password'
```

**JDK 信任库：** `keytool -list -cacerts -storepass changeit` 用文档里的初始密码列出默认信任库。

**Spring Boot：**

```properties
server.ssl.key-store=file:/opt/app/keystore.p12
server.ssl.key-store-type=PKCS12
server.ssl.key-store-password=${KEYSTORE_PASSWORD}
```

**国内云证书：** 从阿里云、腾讯云等平台下载 Tomcat 或 JKS 格式的证书时，密钥库密码以平台提供的为准，不要沿用自己习惯的密码。下载后尽量直接解压到服务器，不要经过会改动文件内容的中转方式。

想从 PEM 文件生成 PKCS12，见 [PKCS#12 速查](https://ssl.sh/zh/cookbook/pkcs12/)或用 [SSL 格式转换工具](https://ssl.sh/zh/tools/ssl-converter/)；用[私钥匹配工具](https://ssl.sh/zh/tools/key-matcher/)确认私钥和证书是一对。

## 不推荐：关闭证书校验

信任库打不开时改装“信任所有证书”的 `TrustManager`，等于取消服务器身份验证。有人打算“等找到密码再改回来”，这期间任何证书都会被接受，账号密码和数据都可能被截获。同样，不要为了部署方便把密码明文提交到代码仓库。

更安全的替代办法：用 `keytool -importcert` 把需要的 CA 证书导入一个新的信任库；密码放进密钥管理服务或环境变量；统一转换成 PKCS12，让各种工具对文件的处理方式一致。

## 怎么确认已经修好

修好的标准是：keytool 用应用的同一个密码能打开文件，应用启动时不再报这个 IOException。

```bash
keytool -list -keystore /opt/app/keystore.p12 -storepass "$KEYSTORE_PASSWORD" >/dev/null && echo "keystore OK"
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -enddate
```

如果是服务器密钥库，443 端口出示的证书应该就是你预期的那一张，可以再用 [SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)从外部确认。
