File list-apt-key-expiry-date of Package kimi-utils-ubuntu
#!/usr/bin/env bash
# List APT key files (.gpg and .asc), show their expiry dates and how many days remain.
# Keys with no expiry are reported as "This key never expires."
set -euo pipefail
IFS=$'\n\t'
# Force C locale so gpg shows predictable English tokens
export LANG=C
export LC_ALL=C
# Options
USE_COLORS=true
for arg in "$@"; do
case "$arg" in
--no-colors) USE_COLORS=false ;;
*) printf 'Unknown option: %s\n' "$arg" >&2; exit 2 ;;
esac
done
# Colours
if $USE_COLORS && tput setaf 1 &>/dev/null; then
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
NC=$(tput sgr0)
else
RED='' GREEN='' NC=''
fi
readonly KEYRING_DIRS=(
"/etc/apt/trusted.gpg.d"
"/etc/apt/keyrings"
"/usr/share/keyrings"
)
readonly COL1_W=8
readonly COL2_W=60
wrap() { fmt -w "$COL2_W" <<< "$1"; }
print_header() {
printf "%-${COL1_W}s %s\n" "Key Type" "Description"
printf "%-${COL1_W}s %s\n" "--------" "-----------"
}
print_row() {
local key_type=$1 description=$2 wrapped
wrapped=$(wrap "$description")
printf "%-${COL1_W}s %s\n" "$key_type" "$(head -n1 <<< "$wrapped")"
tail -n +2 <<< "$wrapped" | sed "s/^/$(printf '%*s' $((COL1_W + 2)) '')/"
}
found_any=false
current_sec=$(date +%s)
echo "Listing APT keys and their expiry dates:"
echo "-----------------------------------------"
echo
print_header
print_row "SC" 'Signing & Certification (can sign other keys).'
echo
print_row "SCEAR" 'Signing, Certification, Encryption, Authentication, Revocation.'
echo
shopt -s nullglob
for KEYRING_DIR in "${KEYRING_DIRS[@]}"; do
if [[ ! -d "$KEYRING_DIR" ]]; then
printf 'Keyring directory %s does not exist.\n' "$KEYRING_DIR" >&2
continue
fi
for key_file in "$KEYRING_DIR"/*.{gpg,asc}; do
if [[ -e "$key_file" ]]; then
found_any=true
printf '%b\n' "${GREEN}${key_file}${NC}"
line_number=0
# Use gpg --show-key for a human-readable key dump; parse "pub" lines
while IFS= read -r line; do
line_number=$((line_number + 1))
if [[ $line == pub* ]]; then
# Colorize expired token if present
if [[ $line == *expired* ]] && $USE_COLORS; then
line=${line//expired/${RED}expired${NC}}
fi
printf '%b\n' "$line"
# Extract expiry information robustly.
expiry_date=''
# Check for expiry information
if [[ $line =~ ([Ee]xpires|[Ee]xpired)[[:space:]]*:[[:space:]]*([0-9]{4}-[0-9]{2}-[0-9]{2}) ]]; then
expiry_date="${BASH_REMATCH[2]}"
elif [[ $line =~ [^]]*([Ee]xpires|[Ee]xpired)[^]]*:[[:space:]]*([0-9]{4}-[0-9]{2}-[0-9]{2})[^]]* ]]; then
expiry_date="${BASH_REMATCH[2]}"
fi
if [[ -z $expiry_date ]]; then
# Find all YYYY-MM-DD tokens in the line
mapfile -t dates < <(grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' <<< "$line" || true)
if (( ${#dates[@]} )); then
expiry_date="${dates[-1]}"
# Check for labels to infer if this date is the expiry date
if (( ${#dates[@]} == 1 )) && ! [[ $line =~ ([Ee]xpires|[Ee]xpired|expire) ]]; then
expiry_date=''
fi
fi
fi
# If still empty => key never expires.
if [[ -z $expiry_date ]]; then
echo "This key never expires."
continue
fi
# Parse expiry_date robustly; accept YYYYMMDD or YYYY-MM-DD and some time variants.
target_sec=''
target_sec=$(date -d "$expiry_date" +%s 2>/dev/null || true)
if [[ -z $target_sec ]]; then
# Try to normalize YYYYMMDD -> YYYY-MM-DD
normalized=$(sed 's/[^0-9]//g' <<< "$expiry_date")
if [[ $normalized =~ ^([0-9]{4})([0-9]{2})([0-9]{2})$ ]]; then
normalized="${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}"
target_sec=$(date -d "$normalized" +%s 2>/dev/null || true)
fi
fi
if [[ -z $target_sec ]]; then
printf 'Could not parse expiry date: %s\n' "$expiry_date"
continue
fi
diff_sec=$(( target_sec - current_sec ))
# Use floor division for negative values too
if (( diff_sec >= 0 )); then
days_left=$(( (diff_sec + 86399) / 86400 )) # round up partial days
else
days_left=$(( diff_sec / 86400 )) # negative or zero
fi
if (( diff_sec < 0 )); then
overdue_days=$(( (-diff_sec + 86399) / 86400 ))
printf 'The date %s is already past (%d day(s) overdue).\n' "$expiry_date" "$overdue_days"
elif (( days_left == 0 )); then
echo "This key expires today."
else
printf '%d day(s) left until %s.\n' "$days_left" "$expiry_date"
fi
elif [[ $line_number -eq 2 ]]; then
# Print signature/user-id line (trimmed)
trimmed="${line#"${line%%[![:space:]]*}"}"
trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
printf 'signature: %s\n' "$trimmed"
fi
done < <(LANG=C gpg --show-key "$key_file" 2>/dev/null)
echo "-----------------------------------------"
fi
done
done
if ! $found_any; then
printf 'No .gpg or .asc files found in the specified keyring directories.\n' >&2
exit 1
fi