How to obtain value of "x5t" using Certificate credentials for application authentication
Asked Answered
V

6

7

I was trying to obtain JWT token from Microsoft Azure Active Directory using Certificate credentials for application authentication.

I am struck at figuring out the value of "x5t".

I have tried with

  • SHA-1 fingerprint value available in the public certificate.
  • SHA-1 hash of the public certificate using FVIC.

But i keep getting below error when send the request to MSA login endpoint

{
    "error": "invalid_client",
    "error_description": "AADSTS70002: Error validating credentials. AADSTS50012: Client assertion contains an invalid signature. [Reason - The key was not found., Thumbprint of key used by client: '6F67F76B96F6FBBDF9D3EE1DDF7F9A7B877EE9C75DEDBD3DE9C7FB', Configured keys: [Key0:Start=06/01/2018, End=12/31/2099, Thumbprint=6WGktXA64QmA9TPv;Key1:Start=06/01/2018, End=12/31/2099, Thumbprint=rD9Q10sR6Q6ZkDVw;]]\r\nTrace ID: d9e3e276-e878-4b8a-b08b-10c82a0b0600\r\nCorrelation ID: 48ec889d-2376-45a6-9bf0-01b22b0e0c17\r\nTimestamp: 2018-06-01 09:38:24Z",
    "error_codes": [
        70002,
        50012
    ],
    "timestamp": "2018-06-01 09:38:24Z",
    "trace_id": "d9e3e276-e878-4b8a-b08b-10c82a0b0600",
    "correlation_id": "48ec889d-2376-45a6-9bf0-01b22b0e0c17"
}

How to obtain the value for "x5t" ?

Vinous answered 2/6, 2018 at 13:35 Comment(0)
H
19

I found this site and this one invaluable for solving the x5t issue. The easiest way to do it is to manually get the fingerprint:

echo $(openssl x509 -in your.cert.pem -fingerprint -noout) | sed 's/SHA1 Fingerprint=//g' | sed 's/://g' | xxd -r -ps | base64

the value from the above command is the value you put in the x5t field in the JWT. Prior to that I was getting invalid fingerprint error from azure.

If you're using Ruby you can follow this answer to get:

p12 = OpenSSL::PKCS12.new(File.read(CERT_FILE), '')
x509_sha1_thumbprint = Base64.encode64(OpenSSL::Digest::SHA1.new(p12.certificate.to_der).digest).strip
jwt_token = JWT.encode payload, p12.key, 'RS256', { typ: 'JWT', x5t: x509_sha1_thumbprint }
Hansel answered 3/10, 2018 at 10:35 Comment(5)
Thank you for the explanation. On my Mac+Chrome, the scroll bar is kind of annoying when I want to read the (first) command (where you retrieve the fingerprint). I cannot read properly because when I scroll the scroll bar stays. Assuming a lot of Mac+Chrome users encounter the same problem, I'd say editing it somehow (i.e. with a new line after the command) would improve readability.Sora
on my Mac+Chrome it scrolls okHansel
What do you consider „ok“? Because the scroll is fine, it’s the bar that is positioned quite bad :)Sora
I used this approach and it worked. You saved me quite some work @codebrane!Verso
There's a slightly simpler way to generate the digest without parsing hex: OpenSSL::Digest::SHA1.new(p12.certificate.to_der).digestBoggers
R
2

The x5t should be the X509 certificate's SHA-1 thumbprint, base64url-encoded:

4.1.7. "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter

The "x5t" (X.509 certificate SHA-1 thumbprint) Header Parameter is a base64url-encoded SHA-1 thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate [RFC5280] corresponding to the key used to digitally sign the JWS. Note that certificate thumbprints are also sometimes known as certificate fingerprints. Use of this Header Parameter is OPTIONAL.

Source: RFC7515 — https://www.rfc-editor.org/rfc/rfc7515#section-4.1.7

Relict answered 3/6, 2018 at 9:40 Comment(1)
Hi @evilSnobu, Thank for the info. However i am still not able to figure out how Microsoft could get the values published in "x5t" listed here. If i copy the value of "x5c" at any online X.509 decoder, they doesn't match with the processed value from certificate's hash.Vinous
C
1

This Python code will output the correct value and you can put it into any online compiler:

import base64
import binascii

hash_hex = "CERT_THUMBPRINT"
hash_bytes = binascii.unhexlify(hash_hex)
hash_base64url = base64.urlsafe_b64encode(hash_bytes).rstrip(b'=').decode('utf-8')

print(hash_base64url)
Catachresis answered 7/12, 2023 at 18:40 Comment(0)
A
1

Here is how you can compute x5t from a given certificate:

-----BEGIN CERTIFICATE-----
BLABSDBLSADNLDLK
-----END CERTIFICATE-----

use the following:

import base64
import hashlib
import jwt
from cryptography.hazmat.primitives._serialization import Encoding
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
from cryptography.x509 import load_pem_x509_certificate


def calculate_jwks_from_certificate(certificate: bytes) -> dict[str, list[dict]]:
    """
    Calculate jwks from given certificate
    :param certificate: content of certificate
    :return: dict with jwks
    """
    cert_obj = load_pem_x509_certificate(certificate, default_backend())
    jwk = cert_obj.public_key()
    public_numbers = jwk.public_numbers()
    # Get the DER-encoded version of the certificate
    cert_der = cert_obj.public_bytes(encoding=Encoding.DER)
    # Compute the SHA-1 hash of the DER-encoded certificate
    hash_sha1 = hashlib.sha1(cert_der).digest()
    # Base64Url encode the hash
    x5t = base64.urlsafe_b64encode(hash_sha1).rstrip(b'=').decode()
    return {
        "keys": [
            {
                "x5t": x5t,
                "n": public_numbers.n,
                "e": public_numbers.e,
            }
        ]
    }
Assr answered 18/4, 2024 at 7:33 Comment(0)
J
0

In Powershell 7.x, this goes like this. Important: Do not use the thumbprint from the cert to calculate x5t but instead do it like below,

$CertThumbPrint = '<Thumbprint of your cert with private key, stored in machine/Personal'
$CertificatePath = "cert:\localmachine\my\$CertThumbPrint" #<Add the Certificate Path Including Thumbprint here e.g. cert:\currentuser\my\6C1EE1A11F57F2495B57A567211220E0ADD72DC1 >#
$Cert = Get-Item -Path $CertificatePath

# *** use this (and comment the above line) if you want to load the cert from a pfx file instead of getting it from the machine cert store
#$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2((join-path -path $PSScriptRoot -ChildPath "ExtractContractsFrom-ITSystemMBX.pfx"),"<PFX password>")

$CertificateBase64Hash = [System.Convert]::ToBase64String($cert.GetCertHash())

# Use the CertificateBase64Hash and replace/strip to match web encoding of base64
$x5t = $CertificateBase64Hash -replace '\+','-' -replace '/','_' -replace '='
Jolie answered 27/8, 2022 at 0:26 Comment(0)
S
0

When I used the below command, it was not generating the correct fingerprint because my default fingerprint output was SHA256 and not SHA1.

echo $(openssl x509 -in your.cert.pem -fingerprint -noout) | sed 's/SHA1 Fingerprint=//g' | sed 's/://g' | xxd -r -ps | base64

So I updated the command to output fingerprint specifically in SHA1 as below, and it worked. Same command also worked for .crt file format of certificate:

echo $(openssl x509 -in your.cert.pem -fingerprint -sha1 -noout) | sed 's/SHA1 Fingerprint=//g' | sed 's/://g' | xxd -r -ps | base64

Another way to get the fingerprint for Microsoft Azure is to utilize the thumbprint generated after uploading of the certificate. Use the thumbprint column value to generate it. enter image description here

echo "THUMBRINT COLUMN VALUE" |xxd -r -p| openssl enc -a

OR

echo "THUMBRINT COLUMN VALUE" |xxd -r -p| base64
Sobriquet answered 2/5, 2023 at 6:45 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.