#1142266 ca-certificates-java: doesn't refresh /etc/ssl/certs/java/cacerts on upgrade from bookworm

#1142266#5
Date:
2026-07-17 03:32:50 UTC
From:
To:
Package: ca-certificates-java
Version: 20240118
Severity: serious
Tags: security
Justification: obsolete root CAs for Java applications
X-Debbugs-Cc: Debian Security Team <team@security.debian.org>

Hi,

I'm helping a number of customers upgrade their Debian systems from
stable releases to stable releases, and I have a compare-etc script that
checks how /etc compare between a fresh Debian 13 system and a system
upgraded from Debian 12.

On the ca-certificates side, everything looks good: ca-certificates.crt
is identical (modulo the order in which certificates appear).

On the ca-certificates-java side, that's not the case: cacerts has been
left unchanged by the upgrade.

This doesn't appear to be specific to those specific systems, as I'm
able to reproduce the bug with a fresh bookworm debootstrap, installing
ca-certificates{,-java}, switching apt configuration to trixie, and
upgrading both packages. The cacerts file is left unchanged, and
comparing the certificates between upgraded bookworm and fresh trixie
gives the same number of additions/deletions as reported by
ca-certificates during the upgrade.

To reproduce:

 - install bookworm:

    cd /tmp
    sudo debootstrap bookworm bookworm
    sudo mount --bind /proc bookworm/proc
    sudo chroot bookworm apt-get install -y default-jre ca-certificates-java

 - partial upgrade from bookworm to trixie:

    sudo sed s/bookworm/trixie/ -i bookworm/etc/apt/sources.list
    sudo chroot bookworm apt-get update
    sudo chroot bookworm apt-get install -y ca-certificates ca-certificates-java

 - upgrade output:

    Preparing to unpack .../ca-certificates_20250419_all.deb ...
    Unpacking ca-certificates (20250419) over (20230311+deb12u1) ...
    Preparing to unpack .../ca-certificates-java_20240118_all.deb ...
    Unpacking ca-certificates-java (20240118) over (20230710~deb12u1) ...
    Setting up ca-certificates (20250419) ...
    Updating certificates in /etc/ssl/certs...
    rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
    21 added, 13 removed; done.
    Setting up ca-certificates-java (20240118) ...
    done.
    Processing triggers for ca-certificates (20250419) ...
    Updating certificates in /etc/ssl/certs...
    0 added, 0 removed; done.
    Running hooks in /etc/ca-certificates/update.d...
    done.
    Processing triggers for ca-certificates-java (20240118) ...
    done.

 - install trixie:

    cd /tmp
    sudo debootstrap trixie trixie
    sudo mount --bind /proc trixie/proc
    sudo chroot trixie apt-get install -y default-jre ca-certificates-java

To compare:

    echo changeit | sudo chroot bookworm keytool -list -keystore /etc/ssl/certs/java/cacerts 2>/dev/null | grep ^Certificate | sort > bookworm.txt
    echo changeit | sudo chroot trixie keytool -list -keystore /etc/ssl/certs/java/cacerts 2>/dev/null | grep ^Certificate | sort > trixie.txt
    diff -u bookworm.txt trixie.txt|tail -n +3|grep -c ^+
    diff -u bookworm.txt trixie.txt|tail -n +3|grep -c ^-

which yields 21 additions and 13 deletions, which matches what's
happening at the ca-certificates level when upgrading.


According to my notes, we already had to implement a workaround when
upgrading from Debian 10 to Debian 11 (tracked as #922981), but that
wasn't the case when upgrading from Debian 11 to Debian 12.

I haven't tried to understand exactly what's happening here yet, the
levels of indirection are a little intimidating…


Cheers,

#1142266#10
Date:
2026-07-17 04:48:47 UTC
From:
To:
Cyril Brulebois <cyril@debamax.com> (2026-07-17):
[…]

Initial investigation
=====================

 - postinst enables “fresh” on initial installation and also when
   upgrading from specific, very old versions; that's definitely not the
   case here.
 - update-ca-certificates isn't called with --fresh during the upgrade;
 - therefore I'm quite certain there's no chance to enter the first
   “then” of this block:
https://sources.debian.org/src/ca-certificates-java/20240118/debian/ca-certificates-java.postinst#L119-L123
 - in the context of an upgrade, it would make sense to go through a
   to-do list left behind by the hook, which would be the “pending” file
   as I understand it, and that'd go through the second “then” of the
   aforementioned block.

Now, I don't have any definitive proof yet, but seeing how the upgrade
output gives two update runs (cherry-picking lines from the log above):

    Updating certificates in /etc/ssl/certs...
    21 added, 13 removed; done.
    […]
    Updating certificates in /etc/ssl/certs...
    0 added, 0 removed; done.

for a single hook run, after the second update run:

    Running hooks in /etc/ca-certificates/update.d...

It seems safe to assume this hook run doesn't see any changes, and
leaves an empty (or maybe missing) “pending” file. If that hook had run
twice, the first run might have generated an interesting “pending” file
(with the additions/deletions), while the second one would have been a
no-op (the hook uses >> to append to the pending file, it doesn't start
from scratch, so a second hook run wouldn't reset what was generated by
the first one).

Redoing the upgrade under strace confirms we go through all of the
update_certs() function twice, with the hook running in-between,
creating an empty “pending” file via:

    cat - >> /var/lib/ca-certificates-java/pending

which is confirmed by the very few syscalls performed by PID 74084 (not
pictured below), and by st_size=0 on the last newfstatat() call:

    73273 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/convert_pkcs12_keystore_to_jks", 0x7ffdf65011b0, 0) = -1 ENOENT (No such file or directory)
    73273 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/fresh", 0x7ffdf6501200, 0) = -1 ENOENT (No such file or directory)
    73273 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/fresh", 0x7ffdf6501250, 0) = -1 ENOENT (No such file or directory)
    73273 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/pending", 0x7ffdf6501200, 0) = -1 ENOENT (No such file or directory)
    73297 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/fresh", 0x55e4ea327c70, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
    73298 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/pending", 0x562f3f97fc70, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)

    74081 openat(AT_FDCWD, "/var/lib/ca-certificates-java/pending", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
    74081 fcntl(1, F_DUPFD, 10)             = 11
    74081 close(1)                          = 0
    74081 fcntl(11, F_SETFD, FD_CLOEXEC)    = 0
    74081 dup2(3, 1)                        = 1
    74081 close(3)                          = 0
    […]
    74084 execve("/usr/bin/cat", ["cat", "-"] […]

    74089 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/convert_pkcs12_keystore_to_jks", 0x7ffe43dfba90, 0) = -1 ENOENT (No such file or directory)
    74089 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/fresh", 0x7ffe43dfbae0, 0) = -1 ENOENT (No such file or directory)
    74089 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/fresh", 0x7ffe43dfbb30, 0) = -1 ENOENT (No such file or directory)
    74089 newfstatat(AT_FDCWD, "/var/lib/ca-certificates-java/pending", {st_dev=makedev(0xfd, 0x1), st_ino=2363271, st_mode=S_IFREG|0644, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=0, st_size=0, st_atime=1784260504 /* 2026-07-17T05:55:04.578209913+0200 */, st_atime_nsec=578209913, st_mtime=1784260504 /* 2026-07-17T05:55:04.578209913+0200 */, st_mtime_nsec=578209913, st_ctime=1784260504 /* 2026-07-17T05:55:04.578209913+0200 */, st_ctime_nsec=578209913}, 0) = 0

https://sources.debian.org/src/ca-certificates-java/20240118/debian/ca-certificates-java.postinst#L62-L128

All of this confirms why we end up with a no-op regarding the cacerts
file.

I'm not sure what a fix would look like. Running hooks after each update
might be risky, because it could lead to trigger cycles or something?


Possible workaround
===================

In the meanwhile, `update-ca-certificates --fresh` might be tempting,
but it's not sufficient: it only ensures all current certificates get
added to cacerts, it doesn't reflect deletions. Removing the cacerts
file beforehands makes it possible to finally get in sync with what a
fresh trixie system looks like:

    rm -f /etc/ssl/certs/java/cacerts
    update-ca-certificates --fresh

The 13 deletions, resulting in 149 entries total:

    -debian:autoridad_de_certificacion_firmaprofesional_cif_a62634068_2.pem, Jul 17, 2026, trustedCertEntry,
    -debian:entrust_root_certification_authority_-_g4.pem, Jul 17, 2026, trustedCertEntry,
    -debian:e-tugra_certification_authority.pem, Jul 17, 2026, trustedCertEntry,
    -debian:e-tugra_global_root_ca_ecc_v3.pem, Jul 17, 2026, trustedCertEntry,
    -debian:e-tugra_global_root_ca_rsa_v3.pem, Jul 17, 2026, trustedCertEntry,
    -debian:hongkong_post_root_ca_1.pem, Jul 17, 2026, trustedCertEntry,
    -debian:securesign_rootca11.pem, Jul 17, 2026, trustedCertEntry,
    -debian:security_communication_rootca3.pem, Jul 17, 2026, trustedCertEntry,
    -debian:security_communication_root_ca.pem, Jul 17, 2026, trustedCertEntry,
    -debian:swisssign_silver_ca_-_g2.pem, Jul 17, 2026, trustedCertEntry,
    -debian:trustcor_eca-1.pem, Jul 17, 2026, trustedCertEntry,
    -debian:trustcor_rootcert_ca-1.pem, Jul 17, 2026, trustedCertEntry,
    -debian:trustcor_rootcert_ca-2.pem, Jul 17, 2026, trustedCertEntry,

(out of 150 because of some encoding fun, but that's another story:
/etc/ssl/certs/NetLock_Arany_=Class_Gold=_F?tan?s?tv?ny.pem)


Cheers,