As you know, Azure Active Directory offers quite a big range of solution for Single Sign-On (SSO) with out-of-the-box capabilities and features that are quite good. But once the initial setup has been done, you will then need to manage all the applications that were on-boarded into it and that can spice things up, especially if you need to work in environments that require short-lived certificate expiry.

In this blog, I will talk about SAML2 Signing Certificates (which requires a PFX), but it would also apply to other solutions such as OAUTH2 or OpenID Connect (which require a CRT/PEM). When on-boarding a new application to AzureAD SAML2 SSO, you will need to define what kind of trust you need and if I’m not wrong, that is explained in the documentation (https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/certificate-signing-options). For the SAML2 exchanges between the Identity Provider (AzureAD) and the Service Provider (the resource that needs SSO), the assertion and response can be signed and that obviously requires a certificate. By default, AzureAD will generate a new, dedicated Signing Certificate for this application, that is valid for 3 years if I recall.

Now let’s assume that you are working at a customer that requires all certificates, including the signing ones, to be valid for 6 months at most, that you have not 1 but 50 applications to manage (that’s still not that much), that each of them requires SSO on DEV/QA/PROD and that of course, you don’t have any way to automate the replacement on AzureAD side because it’s another team that manages it. What would you do in this case? Well, it’s either you perform between 2/3 Signing Certificate replacement every day, or you need to not use the default certificates generated by AzureAD…

I had such case at a customer for which we managed several (on-prem) Kubernetes clusters on which were hosted ECM applications that required SAML2 SSO. Instead of using the AzureAD default signing certificate that is generated by AzureAD on the fly and that needs to be exchanged (through mail usually…), there is the second solution of using a custom certificate through the “Import Certificate” feature. With the custom signing certificate, it would be possible to reduce the number of certificates to manage to a decent amount that wouldn’t compromise the security too much. For example, each application could re-use the same signing certificate for their DEV/QA/PROD environments which would divide by 3 the number of certificates to manage. Or all DEVs could use the same signing certificate and then unique ones for QA/PROD. That is of course up to discussion with the customer and security architects to find what makes sense from a practical and security point of view.

In any cases, you decided to create custom signing certificates, so how could that be done then? Well, it depends on the technologies that you have available, but it could be through simple shell scripts, through Ansible playbooks with passwords managed in vault, through custom containers/service that you would create, etc… If you don’t have access to AzureAD, you will anyway not be able to fully automate the replacement (which is possible through PowerShell it seems). In this blog, since I only want to show the creation of the custom Signing Certificate, I will use a simple shell script that I created some years ago and that I still use from time to time.

This simple script can be used to generate a new key (.key) as well as a new certificate (.crt), based on a request config file, which can be used for OAUTH2/OpenID Connect. Then it will transform the PEM-based certificate into a PFX for the SAML2 option. It’s possible to add much more configuration in the request but an example of content is provided with some configuration that usually makes sense.

[morgan@srv01 ~]$ cd ssl
[morgan@srv01 ssl]$ cat create-cert.sh
#!/bin/bash
############################################################
# Author: Morgan Patou (dbi services)
# Creation Date: 05-Feb-2013
# Version: 1.0
#
# History:
#    1.0 - 05-Feb-2013: Creation of script
#
############################################################

ssl_folder="$HOME/ssl"
mkdir -p ${ssl_folder}

if [[ -f "${ssl_folder}/req.conf" ]]; then
  cert_name="`grep ^CN ${ssl_folder}/req.conf | sed 's,CN[[:space:]]*=[[:space:]]*,,'`"
  # Generate .key and .crt
  openssl req -x509 -nodes -days 190 -newkey rsa:4096 -keyout "${ssl_folder}/${cert_name}.key" -out "${ssl_folder}/${cert_name}.crt" -config "${ssl_folder}/req.conf" -extensions 'v3_req'
  # Transform .key and .crt into .pfx
  openssl pkcs12 -export -out "${ssl_folder}/${cert_name}.pfx" -inkey "${ssl_folder}/${cert_name}.key" -in "${ssl_folder}/${cert_name}.crt"
else
  echo "INFO - Please create the file '${ssl_folder}/req.conf' first and re-execute this script afterwards."
  echo ""
  echo "Example of content:"
  echo "-------------------"
  echo "[req]"
  echo "distinguished_name = dn"
  echo "x509_extensions = v3_req"
  echo "prompt = no"
  echo ""
  echo "[dn]"
  echo "C = CH"
  echo "ST = JU"
  echo "L = Delemont"
  echo "O = dbi services"
  echo "OU = IT"
  echo "CN = dms.poc.it.dbi-services.com"
  echo ""
  echo "[v3_req]"
  echo "keyUsage = keyEncipherment, dataEncipherment"
  echo "extendedKeyUsage = serverAuth"
  echo "subjectAltName = @alt_names"
  echo ""
  echo "[alt_names]"
  echo "DNS.1 = dms.poc.it.dbi-services.com"
  echo "DNS.2 = alfresco1.it.dbi-services.com"
  echo "DNS.3 = alfresco2.it.dbi-services.com"
fi
[morgan@srv01 ssl]$
[morgan@srv01 ssl]$ chmod 750 create-cert.sh
[morgan@srv01 ssl]$
[morgan@srv01 ssl]$ ./create-cert.sh
INFO - Please create the file '$HOME/ssl/req.conf' first and re-execute this script afterwards.

Example of content:
-------------------
[req]
distinguished_name = dn
x509_extensions = v3_req
prompt = no

[dn]
C = CH
ST = JU
L = Delemont
O = dbi services
OU = IT
CN = dms.poc.it.dbi-services.com

[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = dms.poc.it.dbi-services.com
DNS.2 = alfresco1.it.dbi-services.com
DNS.3 = alfresco2.it.dbi-services.com
[morgan@srv01 ssl]$
[morgan@srv01 ssl]$ ls -l
total 4
-rwxr-x---. 1 morgan users 1624 Nov 23 13:35 create-cert.sh
[morgan@srv01 ssl]$

Since what we want is a Signing Certificate and not a WebServer SSL Certificate, we don’t need the CN (or the SAN) to be a HostName/DNS, it can just be something that makes sense. There won’t be any hostname verification, the certificate will just be used by the AzureAD to sign the exchanges. Therefore, let’s create a config file and launch the script:

[morgan@srv01 ssl]$ echo '
[req]
distinguished_name = dn
x509_extensions = v3_req
prompt = no

[dn]
C = CH
ST = JU
L = Delemont
O = dbi services
OU = IT
CN = DEV_AzureAD_SAML2

[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = DEV_AzureAD_SAML2
' > req.conf
[morgan@srv01 ssl]$
[morgan@srv01 ssl]$ ls -l
total 8
-rwxr-x---. 1 morgan users 1624 Nov 23 13:35 create-cert.sh
-rw-------. 1 morgan users  303 Nov 23 13:38 req.conf
[morgan@srv01 ssl]$
[morgan@srv01 ssl]$ ./create-cert.sh
Generating a RSA private key
.......................++++
....................................................++++
writing new private key to '$HOME/ssl/DEV_AzureAD_SAML2.key'
-----
Enter Export Password:
Verifying - Enter Export Password:
[morgan@srv01 ssl]$
[morgan@srv01 ssl]$ ls -l
total 24
-rwxr-x---. 1 morgan users 1624 Nov 23 13:35 create-cert.sh
-rw-------. 1 morgan users 2025 Nov 23 13:38 DEV_AzureAD_SAML2.crt
-rw-------. 1 morgan users 3272 Nov 23 13:38 DEV_AzureAD_SAML2.key
-rw-------. 1 morgan users 4205 Nov 23 13:39 DEV_AzureAD_SAML2.pfx
-rw-------. 1 morgan users  303 Nov 23 13:38 req.conf
[morgan@srv01 ssl]$
[morgan@srv01 ssl]$ # Don't need the .key or .crt for SAML2, so removing these files
[morgan@srv01 ssl]$ rm DEV_AzureAD_SAML2.crt DEV_AzureAD_SAML2.key
[morgan@srv01 ssl]$

The PFX creation command will request for a password as you can see on the prompt, so the PFX and its password need to be shared with the AzureAD SAML2 team so that they can update whatever app they need with this newly generated PFX. In the end, this PFX creation can be automated rather easily so that it refreshes your needed Service Provider(s) with the certificate to decrypt the SAML2 communication and the alignment on AzureAD side will be required to make sure both sides can communicate together.