Posted on 12-16-2019 12:48 PM
Hello,
Is there a way to delete duplicate certificates? - Today we push out our AD & Wifi certificate with a configuration profile, and had to do some minor changes on the settings for the certificate.
The result is we now have two certificates with exactly same name $SERIALNUMBER.Domain.Companyname.com
and we would have to delete the older one, something like "...delete certificates created before December 16th.."
Is there any "smooth"" way to do this?
Solved! Go to Solution.
Posted on 12-18-2019 05:55 AM
Following script worked like charm for me!
Thank you @AdamMartin
#!/bin/bash
# This script will remove all instances of a system keychain cert where:
# 1) The certificate subject matches the cert subject below.
# 2) It does not have the latest expiration date.
certSubject="Common-text-here i.e domain name"
certList=$( security find-certificate -c "${certSubject}" -p -a )
# echo "$certList"
# exit
# Get each cert into an array element
# Remove spaces
certList=$( echo "$certList" | sed 's/ //g' )
# Put a space after the end of each cert
certList=$( echo "$certList" | sed 's/-----ENDCERTIFICATE-----/-----ENDCERTIFICATE----- /g' )
# echo "$certList"
OIFS="$IFS"
IFS=' '
# read -a certArray <<< "${certList}"
declare -a certArray=($certList)
IFS="$OIFS"
i=-1
dateHashList=''
# Print what we got...
for cert in "${certArray[@]}"; do
let "i++"
echo '---------'
# echo "$cert"
# echo '--'
# Fix the begin/end certificate
cert=$( echo "$cert" | sed 's/-----BEGINCERTIFICATE-----/-----BEGIN CERTIFICATE-----/g' )
cert=$( echo "$cert" | sed 's/-----ENDCERTIFICATE-----/-----END CERTIFICATE-----/g' )
# echo "$cert"
# echo "$cert" | openssl x509 -text
certMD5=$( echo "$cert" | openssl x509 -noout -fingerprint -sha1 -inform pem | cut -d "=" -f 2 | sed 's/://g' )
certDate=$( echo "$cert" | openssl x509 -text | grep 'Not After' | sed -E 's|.*Not After : ||' )
certDateFormatted=`date -jf "%b %d %T %Y %Z" "${certDate}" +%Y%m%d%H%M%S`
echo "Cert ${i} : ${certDate} => $certDateFormatted"
echo "Cert ${i} : ${certMD5}"
NL=$'
'
dateHashList="${dateHashList}${NL}${certDateFormatted} ${certMD5}"
done
echo
dateHashList=$( echo "$dateHashList" | sort | uniq )
lines=$( echo "$dateHashList" | wc -l | tr -d ' ' )
let "lines--"
echo "[info] There are $lines lines in the certificate date-hash list."
echo
i=0
OIFS="$IFS"
IFS=$'
' # make newlines the only separator
for dateHash in $dateHashList; do
let "i++"
dateNum="${dateHash%% *}"
hash="${dateHash##* }"
echo "${i}| Hash : "$hash" | dateNum : "$dateNum""
if [[ i -ne $lines ]]; then
echo "=> This cert will be removed"
sudo security delete-certificate -Z $hash /Library/Keychains/System.keychain
echo
else
echo "=> This cert will not be touched because it has the latest expiration date."
fi
done
IFS="$OIFS"
exit 0
Posted on 12-18-2019 05:55 AM
Following script worked like charm for me!
Thank you @AdamMartin
#!/bin/bash
# This script will remove all instances of a system keychain cert where:
# 1) The certificate subject matches the cert subject below.
# 2) It does not have the latest expiration date.
certSubject="Common-text-here i.e domain name"
certList=$( security find-certificate -c "${certSubject}" -p -a )
# echo "$certList"
# exit
# Get each cert into an array element
# Remove spaces
certList=$( echo "$certList" | sed 's/ //g' )
# Put a space after the end of each cert
certList=$( echo "$certList" | sed 's/-----ENDCERTIFICATE-----/-----ENDCERTIFICATE----- /g' )
# echo "$certList"
OIFS="$IFS"
IFS=' '
# read -a certArray <<< "${certList}"
declare -a certArray=($certList)
IFS="$OIFS"
i=-1
dateHashList=''
# Print what we got...
for cert in "${certArray[@]}"; do
let "i++"
echo '---------'
# echo "$cert"
# echo '--'
# Fix the begin/end certificate
cert=$( echo "$cert" | sed 's/-----BEGINCERTIFICATE-----/-----BEGIN CERTIFICATE-----/g' )
cert=$( echo "$cert" | sed 's/-----ENDCERTIFICATE-----/-----END CERTIFICATE-----/g' )
# echo "$cert"
# echo "$cert" | openssl x509 -text
certMD5=$( echo "$cert" | openssl x509 -noout -fingerprint -sha1 -inform pem | cut -d "=" -f 2 | sed 's/://g' )
certDate=$( echo "$cert" | openssl x509 -text | grep 'Not After' | sed -E 's|.*Not After : ||' )
certDateFormatted=`date -jf "%b %d %T %Y %Z" "${certDate}" +%Y%m%d%H%M%S`
echo "Cert ${i} : ${certDate} => $certDateFormatted"
echo "Cert ${i} : ${certMD5}"
NL=$'
'
dateHashList="${dateHashList}${NL}${certDateFormatted} ${certMD5}"
done
echo
dateHashList=$( echo "$dateHashList" | sort | uniq )
lines=$( echo "$dateHashList" | wc -l | tr -d ' ' )
let "lines--"
echo "[info] There are $lines lines in the certificate date-hash list."
echo
i=0
OIFS="$IFS"
IFS=$'
' # make newlines the only separator
for dateHash in $dateHashList; do
let "i++"
dateNum="${dateHash%% *}"
hash="${dateHash##* }"
echo "${i}| Hash : "$hash" | dateNum : "$dateNum""
if [[ i -ne $lines ]]; then
echo "=> This cert will be removed"
sudo security delete-certificate -Z $hash /Library/Keychains/System.keychain
echo
else
echo "=> This cert will not be touched because it has the latest expiration date."
fi
done
IFS="$OIFS"
exit 0
Posted on 12-18-2019 06:58 AM
Thanks for this script! We issue certs to the serial number of the device. Is there a way to put in a variable for the certSubject? I would like to make that the Serial Number of the device. Thank you again! This script is awesome! @Gonzalo @AdamMartin
Posted on 11-02-2020 11:30 PM
This script is awesome. How do you scope this policy? Is there a way a build a Smart Group with clients having duplicated certificates? Actually I found only a way to make a smart group with clients that have installed a common certificate but can not limit this to duplicates.
Posted on 02-14-2021 11:27 PM
Thank you for sharing the script @Gonzalo .
It works like a charm.
Posted on 02-15-2021 03:23 AM
@colorenz shared a updated version of this script, that works better if your certificate has the same name as the computer.
ComputerName=$(/usr/sbin/scutil --get ComputerName)
#LogFile
LogFile="/var/log/adcs-certificates.log"
#Log Function
logger() {
/bin/echo $(date "+%Y-%m-%d %H:%M:%S ") $1 >>"${LogFile}"
/bin/echo $(date "+%Y-%m-%d %H:%M:%S ") $1
}
logger "-------------------------------------"
logger "Start: Check for mutilpe Certificates"
certList=$(security find-certificate -c $ComputerName -p -a)
# Get each cert into an array element
# Remove spaces
certList=$(echo "$certList" | sed 's/ //g')
# Put a space after the end of each cert
certList=$(echo "$certList" | sed 's/-----ENDCERTIFICATE-----/-----ENDCERTIFICATE----- /g')
# echo "$certList"
OIFS="$IFS"
IFS=' '
# read -a certArray <<< "${certList}"
declare -a certArray=($certList)
IFS="$OIFS"
i=-1
dateHashList=''
# Print what we got...
for cert in "${certArray[@]}"; do
let "i++"
# Fix the begin/end certificate
cert=$(echo "$cert" | sed 's/-----BEGINCERTIFICATE-----/-----BEGIN CERTIFICATE-----/g')
cert=$(echo "$cert" | sed 's/-----ENDCERTIFICATE-----/-----END CERTIFICATE-----/g')
# echo "$cert"
# echo "$cert" | openssl x509 -text
certMD5=$(echo "$cert" | openssl x509 -noout -fingerprint -sha1 -inform pem | cut -d "=" -f 2 | sed 's/://g')
certDate=$(echo "$cert" | openssl x509 -text | grep 'Not After' | sed -E 's|.*Not After : ||')
certDateFormatted=$(date -jf "%b %d %T %Y %Z" "${certDate}" +%Y%m%d%H%M%S)
logger "Cert ${i} : ${certDate} => $certDateFormatted"
logger "Cert ${i} : ${certMD5}"
NL=$'
'
dateHashList="${dateHashList}${NL}${certDateFormatted} ${certMD5}"
done
dateHashList=$(echo "$dateHashList" | sort | uniq)
lines=$(echo "$dateHashList" | wc -l | tr -d ' ')
let "lines--"
logger "Info There are $lines lines in the certificate date-hash list."
i=0
OIFS="$IFS"
IFS=$'
' # make newlines the only separator
for dateHash in $dateHashList; do
let "i++"
dateNum="${dateHash%% *}"
hash="${dateHash##* }"
logger "${i}| Hash : "$hash" | dateNum : "$dateNum""
if [[ i -ne $lines ]]; then
logger "=> This cert will be removed"
sudo security delete-certificate -Z $hash /Library/Keychains/System.keychain
logger "=> Cert was $hash removed"
else
logger "=> This cert will not be touched because it has the latest expiration date."
fi
done
IFS="$OIFS"
logger "-------------------------------------"
logger "End: Check for mutilpe Certificates"
Use it together with this EA and create a smart group.
#!/bin/sh
#Get current number of Computer Certificate
#Current ComputerName
ComputerName=$(/usr/sbin/scutil --get ComputerName)
ComputerCertificateCount=$(/usr/bin/security find-certificate -a -c $ComputerName -p -Z "/Library/Keychains/System.keychain" | grep SHA-1 | wc | awk '{print $1}')
echo "ComputerCertificateCount"
echo "<result>$ComputerCertificateCount</result>"
Once again, thanks @colorenz
Posted on 06-01-2021 01:43 PM
Posted on 07-19-2021 07:50 AM
Use with caution. The script looks a lot like one provided to me by Jamf support. I've recently discovered that it does review all certs based on computer name and keeps the one with the longest expire date. Unfortunately, a few Macs have had certs that have the name of the computer, but are self-assigned or issued by things other than our ADCS services. One I stumbled on was a cert that was self-assigned by CUPS. The result is that every ADCS PKI cert was removed but the Self-Assigned remained in place because it had the longest time to expire.