Entra Workload Identities – Trusted Certificate Authorities (public preview)

In the November 2023 – What’s New in Microsoft Entra Identity & Security w/ Microsoft Security CxE identity episode, a public preview feature of Entra Workload ID premium license was presented (link) which was actually announced on November 9th (link). I really love the idea of restricting application key credentials to a predefined list of Certificate Authorities, this is why I thought to write some words about it.

TL;DR
– You can generate a report on current keyCredentials usage (with certificate Issuer data) using the PowerShell script below (Graph Powershell used here) [no extra license needed]
– First, you create a certificateBasedApplicationConfigurations object
– Then you can modify the defaultAppManagementPolicy or create an appManagementPolicy and apply it directly to one or more application objects (for the latter, tutorial below)
– These configurations require Entra Workload ID premium license

Reporting on application key credentials

The linked announcements are highlighting how to set the defaultAppManagementPolicy, but before setting this, you may want to know which applications are using certificates to authenticate and which CA issued these certs. This way, you can first change the certificates to the ones you trust, then you can set up the restriction(s). The following script lists these applications and the Issuer of each certificate (for the sake of simplicity, I use the Invoke-MgGraphRequest command)

#https://learn.microsoft.com/en-us/graph/api/resources/keycredential?view=graph-rest-1.0
Connect-MgGraph

##region keyauthapps
$applications_url= 'https://graph.microsoft.com/beta/applications?$top=100'
$obj_applications = $null
while ($applications_url -ne $null){
    $response = (Invoke-MgGraphRequest -Method GET -Uri $applications_url)
    $obj_applications += $response.value
    $applications_url = $response.'@odata.nextLink'
    }

#filter apps using keycredentials
$keyauthApps = $obj_applications | ? {$_.keycredentials -ne $null}
#read keycredentialsinfo
$KeyAuthApps_creds =foreach ($app in $keyauthApps){
    Invoke-MgGraphRequest -Method GET -Uri https://graph.microsoft.com/beta/applications/$($app.id)?select=keycredentials
}
##region end

##region build report - apps
$report_Apps = foreach ($cred in $KeyAuthApps_creds.keycredentials){
    $tmp_appReference = $null
    $tmp_appReference = $keyauthApps.Where({$_.keycredentials.keyId -eq $cred.keyId})
    [pscustomobject]@{
    KeyIdentifier = $cred.customKeyIdentifier
    KeyDisplayName = $cred.displayname
    KeyStartDateTime = $cred.startDateTime
    KeyEndDateTime = $cred.endDateTime
    KeyUsage = $cred.usage
    KeyType = $cred.type
    Issuer = ([system.security.cryptography.x509certificates.x509certificate2]([convert]::FromBase64String($cred.key))).Issuer
    EntityID = $tmp_appReference.id
    EntityAppId = $tmp_appReference.appid
    EntityType = "application"
    EntityDisplayName = $tmp_appReference.displayname
    }
    }
##region end

$report_Apps | Out-GridView

The result will look like this (yes, I use self-signed certificates in my demo environment 🙈):

Example result from reporting script

Note: the Issuer field may not be 100% reliable as it can be inserted manually when creating the self-signed certificate. The following method will show each certificate in the trust chain ($cred variable comes from the foreach loop above):

$tmp_cert = ([system.security.cryptography.x509certificates.x509certificate2]([convert]::FromBase64String($cred.key)))
$certChain = [System.Security.Cryptography.X509Certificates.X509Chain]::new()
$certChain.Build($tmp_cert)
$certChain.ChainElements.certificate
Example chain of a free Let’s Encrypt certificate

Building the Trusted Certificate Authority policy

To restrict application keyCredentials, the following should be kept in mind (annoncement link again):
– The policy applies only to new credentials, it won’t disable current keys
– At least one root CA needs to be declared and a chain can consist of a max of 10 objects
First, you create a certificateBasedApplicationConfigurations object (~the trusted cert chain)
Next, you can modify the defaultAppManagementPolicy to restrict all keyCredentials to this/these trusted CAs (as demonstrated on the linked page)
OR you can create a separate appManagementPolicy to restrict the trusted CA THEN this policy can be applied directly to one or more applications (steps below)

Creating the certificateBasedApplicationConfigurations object

In this example, I’m going to use Graph Explorer to create the object. As a Windows user, I will simply export my issuing CA’s (F12SUBCA01) certificate and it root CA’s (ROOTCA01) certificate to a Base-64 encoded CER file using the certlm.msc MMC snap-in, open them in Notepad and copy the contents to the Graph Explorer call’s Request body.
Find the issuing CA’s cert, then right-click – All Tasks – Export:

Select “Base-64 encoded X.509 (.CER)” as export file format.

Repeate the same steps for each certificate in the chain.
Now, open the cer files with notepad, remove the ‘—–BEGIN CERTIFICATE—–‘ and ‘—–END CERTIFICATE—–‘ lines and every line-breaks

Or you can use PowerShell:

$cert = get-childitem Cert:\LocalMachine\ca\ | ? {$_.Subject -match "F12SUBCA01"}
[convert]::ToBase64String($cert.RawData)

These values will be used in the payload sent to Microsoft Graph.
CAUTION! Use the beta endpoint for now as it is a preview feature. If you accidentally use the v1.0 endpoint, you will encouter issues (example below)

METHOD: POST
ENDPOINT: beta
URI: https://graph.microsoft.com/beta/certificateAuthoritites/certificateBasedApplicationConfigurations
REQUEST BODY:
{
  "displayName": "F12 Cert Chain",
  "description": "Allowed App certificates issued by F12SUBCA ",
  "trustedCertificateAuthorities": [{
    "isRootAuthority": true,
    "certificate": "<rootCA base64 certificate data>"
  },
  {
    "isRootAuthority": false,
    "certificate": "<subCA base64 certificate data>"
  }]
}
Creating the trustedCA configuration object

If everything was inserted correctly, the response includes an id, take a note of it. If you did not manage to take it, no problem, you can query these configurations as follows:

METHOD: GET
ENDPOINT: beta
URI: https://graph.microsoft.com/beta/directory/certificateAuthorities/certificateBasedApplicationConfigurations
List configuration objects

Note: when you dig further, the CA information can be queried for each configuration id, for example (you can omit the ‘?$select=isRootAuthority,issuer’ part if you want to check the certificate data too:

METHOD: GET
ENDPOINT: beta
URI: https://graph.microsoft.com/beta/directory/certificateAuthorities/certificateBasedApplicationConfigurations/<configurationID>/trustedCertificateAuthorities?$select=isRootAuthority,issuer

Creating the appManagementPolicy object

Now that we have the CA configuration, the next step is to create the appManagementPolicy object (if you are not going to apply it in the defaultAppManagementPolicy). The appManagementPolicy can contain restrictions for passwordCredentials and KeyCredentials. In this example, I’m going to create a policy that prohibits passwordCredentials and restricts key credentials to the trusted CA configuration defined above.

METHOD: POST
ENDPOINT: BETA
URI: https://graph.microsoft.com/v1.0/policies/appManagementPolicies
REQUEST BODY:
{

    “displayName”: “F12 AppManagementPolicy – F12SUBCA allowed only”,

    “description”: “This policy restricts application credentials to certificates issued by F12SUBCA and disables password addition “,

    “isEnabled”: true,

    “restrictions”: {

        “passwordCredentials”: [

            {

                “restrictionType”: “passwordAddition”,

                “maxLifetime”: null

            }

        ],

        “keyCredentials”: [

            {

                “restrictionType”: “trustedCertificateAuthority”,

                “certificateBasedApplicationConfigurationIds”: [

                    “0d60f78e-9916-4db2-9cee-5c8e470a19e9”

                ]

            }

        ]

    }

}

Creating the appManagementPolicy object

Take a note of the id given in the response as it will be used in the final step.

NOTE: if you accidentally use the v1.0 endpoint, you will encounter issues like this:

“Expected property ‘certificateBasedApplicationConfigurationIds’ is not present on resource of type ‘KeyCredentialConfiguration'”

Applying the policy to an application

Finally, the policy needs to be applied to an application, as follows:

METHOD: POST
ENDPOINT: BETA
URI:https://graph.microsoft.com/beta/applications/<objectID of application>/appManagementPolicies/$ref
REQUEST BODY:
{
    "@odata.id": "https://graph.microsoft.com/beta/policies/appmanagementpolicies/<appManagementPolicyID>"
}
Applying the policy to an application

The result for this application:

Uploading a certificate not issued by the trusted CA fails
Adding new client secret option is greyed out

Closing words: it is a bit cumbersome to configure these settings, but the result is purely satisfying 😊 I hope, once it goes GA it will get some graphical interface to ease the process.

Comments are closed.