# Keystore was tampered with, or password was incorrect: fixes

> Java could not verify a JKS keystore's integrity: usually a wrong store password, sometimes a file corrupted by Maven filtering or a botched copy.

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

**`java.io.IOException: Keystore was tampered with, or password was incorrect` means the integrity check Java computed with your password did not match the value stored in the keystore file.** Usually the store password is wrong. Otherwise the file was damaged in transit, most often by Maven resource filtering or a text-mode copy. Real tampering is rare.

## What the error means

The integrity check failed: the password differs from the one used to write the file, or the file changed. A JKS file ends with a keyed digest computed over its contents and the store password. When you load the keystore with a non-null password, the JDK recomputes that digest; if it differs, `JavaKeyStore` throws this exact `IOException`, with an `UnrecoverableKeyException: Password verification failed` as the cause. The JDK cannot tell a wrong password from a modified file, which is why the message names both.

You will usually see it wrapped by keytool or a server:

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

Since JDK 9 the default keystore type is PKCS12, and `keystore.type.compat=true` lets the JKS and PKCS12 types load either format. A PKCS12 file with a wrong password produces a different message, `keystore password was incorrect`. The fixes below apply to both.

## Causes, most common first

A wrong store password is the most common cause.

### 1. The store password is wrong

A typo, a password from another environment, the default `changeit` used for a custom store, or a value mangled before it reaches Java: `$` expanded by the shell, `#` treated as a comment in a properties file, or an unquoted YAML value.

- **Confirm:** test the exact file with the exact password, quoted:

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

If keytool succeeds, the file is fine and the application is receiving a different password or file.

- **Fix:** correct the value where the application reads it (`server.ssl.key-store-password`, Tomcat's `certificateKeystorePassword`, `javax.net.ssl.trustStorePassword` and so on), and quote it in shells and YAML.

### 2. The keystore and truststore settings are mixed up

A common mix-up is `javax.net.ssl.trustStore` pointing at the server keystore, or `trustStorePassword=changeit` used for a custom truststore created with another password.

- **Confirm:** print the JVM options the process runs with (`ps -ef | grep java` or `jcmd <pid> VM.system_properties | grep javax.net.ssl`).
- **Fix:** point each property at the right file with its own password.

### 3. The file was corrupted on the way

Binary files break when a build or transfer treats them as text. The Maven documentation warns that filtering files with binary content will most likely corrupt them. Git line-ending conversion, FTP in ASCII mode, a Git LFS pointer committed instead of the file, or a Kubernetes Secret that is base64-encoded twice cause the same symptom.

- **Confirm:** compare checksums of the original and the deployed file, and check the first bytes:

```bash
sha256sum keystore.jks /opt/app/keystore.jks
xxd -l 4 /opt/app/keystore.jks     # a JKS file starts with feed feed
```

- **Fix:** exclude keystores from filtering, mark them binary in Git, and copy them in binary mode:

```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. The application loads a different file than you think

A relative path resolved against another working directory, or a keystore of the same name packaged inside the JAR, can shadow the one you edited. Use absolute paths and log the resolved path at startup.

### 5. The file was modified

This is rare. If checksums differ and nobody can explain why, treat it as a security incident: rotate the private key and reissue the certificate.

## Fix commands by platform

Test the password with `keytool -list` first; only a file that fails with the correct password needs replacing.

**Linux and macOS:**

```bash
keytool -list -v -keystore /opt/app/keystore.jks -storepass 'the-password' | head -20
keytool -storepasswd -keystore /opt/app/keystore.jks          # change password (needs the old one)
keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.p12 -deststoretype pkcs12
```

**Windows (PowerShell):** use single quotes so `$` is not expanded:

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

**JDK truststore:** `keytool -list -cacerts -storepass changeit` lists the default trust store, using the documented initial password.

**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}
```

To build a PKCS12 file from PEM files, see the [PKCS#12 cookbook](https://ssl.sh/cookbook/pkcs12/) or the [SSL converter](https://ssl.sh/tools/ssl-converter/), and confirm the key belongs to the certificate with the [key matcher](https://ssl.sh/tools/key-matcher/).

## Don't disable certificate verification

Never replace an unreadable truststore with a trust-all `TrustManager`. When the failing file is a truststore, a common shortcut is to drop the truststore settings and install a trust-all `TrustManager` "until the password is found". That removes server authentication entirely: any certificate is accepted, so credentials and data can be intercepted. Likewise, do not commit the password in plain text to make deployments easier.

Safer alternatives: rebuild the truststore from the CA certificates you need with `keytool -importcert`, keep passwords in your secret store or environment, and convert to PKCS12 so all tools handle the file the same way.

## Verify the fix

```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
```

keytool must succeed with the same password the application uses, the application must start without the IOException, and for a server keystore, the certificate presented on port 443 must be the one you expect. Check it from the outside with the [SSL checker](https://ssl.sh/tools/ssl-checker/).
