The letsencrypt_sslcert.p12 file is a PKCS12 format file, which Certbot wouldn’t create automatically for you, but which must have been created by using some kind of script or command to convert from PEM format.
It looks like the PEM-format certificates were renewed successfully, but that nothing has been done to update the .p12 file. If your web server software is using it, then you probably need to repeat the step that generates it.
I think the .pem file came from me trying to use the server.app to renew the cert. I was trying just about anything since I have multiple cert’s on the server for the multiple domains it hosts.
All of the cert’s renewed using the same method as above, but none of them are recognized
What web server do you use and how did you configure it to know which certificates to use? Did you restart or reload the web server after renewing the certificates? (Most don’t notice that certificates have changed unless they’re restarted or told to reload their configuration.)
Hi @MacSupport, do you know why the self-signed certificate steps that you gave are necessary? Why won’t Keychain allow importing the renewed certificates directly?
It’s not so much an issue with the importing, but rather having two certificates with the same FQDN. For some reason, if you don’t remove the old certificate first, the Server.app has a tendency to corrupt the website config file. The self-signed cert trick is just a workaround. I would love to do this all via command line, but I haven’t found the api to do steps 2-5 & 7,8. Right now I have a script that checks for renewals and then imports them into Keychain.
#!/bin/bash
RED='\033[0;31m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
if [[ $UID != 0 ]]; then
echo "Please run this script with sudo:"
echo "sudo $0 $*"
exit 1
fi
read -r -p "Type the domain name you would like to import (example.com), followed by [ENTER]: " thedomain
# bash generate random 32 character alphanumeric string (upper and lowercase)
TEMP_PASS=$(cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
echo "\n\n${YELLOW}Temp password created for import to Keychain Access: ${RED}"$TEMP_PASS"${NC}\n\n"
read -p "Export the $thedomain LetsEncrypt Cert to Keychain Access Format (.p12) [Y or N]: " -n 1 -r
echo # (optional) move to a new line
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
exit 1
fi
eval openssl pkcs12 -export -inkey /etc/letsencrypt/live/$thedomain/privkey.pem -in /etc/letsencrypt/live/$thedomain/cert.pem -certfile /etc/letsencrypt/live/$thedomain/fullchain.pem -out /etc/letsencrypt/live/$thedomain/letsencrypt_sslcert.p12 -passout pass:$TEMP_PASS
read -p "Import the $thedomain LetsEncrypt Cert to Keychain Access [Y or N]: " -n 1 -r
echo # (optional) move to a new line
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
exit 1
fi
eval security import /etc/letsencrypt/live/$thedomain/letsencrypt_sslcert.p12 -f pkcs12 -k /Library/Keychains/System.keychain -P $TEMP_PASS -T /Applications/Server.app/Contents/ServerRoot/System/Library/CoreServices/ServerManagerDaemon.bundle/Contents/MacOS/servermgrd
I guess part of the answer that I was looking for was "by importing them into Keychain". I'm afraid I didn't know about Keychain because I don't use macOS.
If @MacSupport's script or something like it does work properly for you, there's a way that you could combine it with Certbot's autorenewal feature so that it runs automatically when certificates get renewed. The supported way to do this is to add a --renew-hook option to the cron job that called certbot renew, with an additional argument indicating the location of the script. Then that script will be run whenever certificates get renewed by certbot renew.
I guess the script wouldn't work properly that way in its exact current form because it tries to read the domain name interactively from the user with read, but we could create a non-interactive version that can be used with --renew-hook so that unattended renewals from cron result in getting an import into Keychain.
@MacSupport, would you be willing to work on a non-interactive version of this? The --renew-hook invocation method should be able to tell your script via environment variables which certificate was renewed.
Does the security import command require the user to interact with a dialog box or anything, or can it run totally unattended? (I think the eval is superfluous there.)