IllegalAccessException in apksigner with SunPKCS11 on Debian 13 / Java 21

Thore Göbel
2025-12-30T20:29:21Z

On Debian 13 (possibly due to Java 21?), signing with PKCS#11 no longer works.

## Steps to reproduce

- Pre-create the private key and certificate on the Nitrokey HSM. See the workaround described in https://gitlab.com/fdroid/fdroidserver/-/issues/1312.
- `fdroid init`
- Edit the `config.yml` to set the `repo_keyalias, keystore: NONE, keystorepass` and the other relevant options
- `fdroid update`

## System info

- Debian 13 (Trixie)
- Java 21 (from apt)
- fdroidserver 2.4.2 (from stable apt, NO backport)

## Log

The output of `fdroid update`:

```
2025-12-30 20:06:18,420 INFO: Creating signed index with this key (SHA256):
2025-12-30 20:06:18,420 INFO: CC AA FF EE
2025-12-30 20:07:12,758 CRITICAL: Failed to sign repo/entry.jar: Exception in thread "main" java.lang.IllegalAccessException: class com.android.apksigner.ApkSignerTool$ProviderInstallSpec cannot access class sun.security.pkcs11.SunPKCS11 (in module jdk.crypto.cryptoki) because module jdk.crypto.cryptoki does not export sun.security.pkcs11 to unnamed module @2f2c9b19
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:394)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:714)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:495)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
at com.android.apksigner.ApkSignerTool$ProviderInstallSpec.installProvider(ApkSignerTool.java:1249)
at com.android.apksigner.ApkSignerTool$ProviderInstallSpec.access$200(ApkSignerTool.java:1217)
at com.android.apksigner.ApkSignerTool.sign(ApkSignerTool.java:351)
at com.android.apksigner.ApkSignerTool.main(ApkSignerTool.java:94)
```

## Workarounds

I ran into the same problem while trying to sign a single APK manually with `apksigner`.
Here is how I got it to work.

It works, but it feels hacky, so I am very open to better ideas.
Also, I am not sure if this is an intended change in Java 21, or if this should be reported as a bug upstream?

```
# depending on which apksigner you want
export JAR="${HOME}/Android/Sdk/build-tools/35.0.0/lib/apksigner.jar" # source: Android SDK
export JAR="/usr/share/java/apksigner.jar" # source: apt

sudo java --add-exports jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED -jar ${JAR} sign --provider-class sun.security.pkcs11.SunPKCS11 --provider-arg opensc-fdroid.cfg --ks NONE --ks-type PKCS11 --ks-pass stdin --ks-key-alias ${KEY_ALIAS} --in ${APK_IN} --out ${APK_OUT}
```

Important additions:

- `--add-exports jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED`
- Invoking `apksigner.jar` instead of `apksigner` (the shell script that ships with the Android SDK)