How to … Generate a Public Key for the Azure B2C Tenant to verify the JWT

Security Oct 18, 2022

In my last project, I got the requirement to deliver the Public Key for the Azure B2C Tenant so that they can use this to validate the JWT in the target System… UH... Okay, I figured out that there is no public Key available on the Frontend and other Endpoints. But it’s not impossible to generate a Key.

Get the required information

Fortunately Azure B2C will give you the required information to generate the identity just navigate to the URL

https://mytenant.b2clogin.com/{your_tenantname}/b2c_1_susi/discovery/v2.0/keys`

First, I will fetch the two operands, the modulus, and the exponent, from a JWKS (JSON Web Key Set). Here is a partial representation:

{
    "keys": [
        {
            "kty": "RSA",
            "kid": "K14p_n3IB1eTrez6cRjUWB0GOWc",
            "use": "sig",
            "n": "3bmGZLQSl3-wvGZVrZeI-HGz0XjB6F1lcwyxGRNl4GN3c7qHJyK5EiTqBNHKCQ76njmbERvIph8NlrH4G5l8tWUbB5z3vQYBkegc-fK0q7-QAZMJ9GglxjbppeIHpvYlk5G04CNedzyxA4SG2KNPdELgXYZDOVKtmd2jSSVys1P81H7olm1TS_3jTZP9PScY0t0fOibzzOFK7pdpfz6yjMt2FMLwMtLYNcZ2QaBRqFntNZ5biDiSsk60M5f_DwwAAKnDMZ5pT0qDeFMo8JQfhGMhr8a46oNXXbRmLXGUEytbiesQPVaTMpDkWqyxq_pOFhn2Er99i698YS6LNuZpeQ",
            "e": "AQAB"
        }
    ]
}

Where the n the field stands for the modulus and the e the field is the exponent.

Also, from that single JWK, one can observe that the type is RSA and that its purpose is to sign payloads. Refer to the RFC if any doubt 😄

If you were not sure, yes, based on these two attributes, one can compute the public key.

Let's assume these values are given:

export n="3bmGZLQSl3-wvGZVrZeI-HGz0XjB6F1lcwyxGRNl4GN3c7qHJyK5EiTqBNHKCQ76njmbERvIph8NlrH4G5l8tWUbB5z3vQYBkegc-fK0q7-QAZMJ9GglxjbppeIHpvYlk5G04CNedzyxA4SG2KNPdELgXYZDOVKtmd2jSSVys1P81H7olm1TS_3jTZP9PScY0t0fOibzzOFK7pdpfz6yjMt2FMLwMtLYNcZ2QaBRqFntNZ5biDiSsk60M5f_DwwAAKnDMZ5pT0qDeFMo8JQfhGMhr8a46oNXXbRmLXGUEytbiesQPVaTMpDkWqyxq_pOFhn2Er99i698YS6LNuZpeQ"export e="AQAB"

Base64 URL-decode the modulus. I suggest using that short bash script:

#!/usr/bin/env bash
# Encode to / decode from Base64-URL without padding.

# USAGE:
# bash base64url.sh encode 'Hello!'
# bash base64url.sh decode SGVsbG8h

function encode {
  echo -n "$1" | openssl enc -a -A | tr -d '=' | tr '/+' '_-'
}

function decode {
  _l=$((${#1} % 4))
  if [ $_l -eq 2 ]; then _s="$1"'=='
  elif [ $_l -eq 3 ]; then _s="$1"'='
  else _s="$1" ; fi
  echo "$_s" | tr '_-' '/+' | openssl enc -d -a -A
}

case $1 in
  encode) encode "$2" ;;
  decode) decode $2 ;;
  e) encode "$2" ;;
  d) decode $2 ;;
esac


Then call it

# chmod u+x decoder.sh./decoder.sh decode $n > modulus.bin

The next step is, that you must convert it to a hex code with this command:

xxd -ps -c 256 modulus.bin

Repeat the same operations for the exponent:

./decoder.sh decode $e > exponent.binxxd -ps -c 256 exponent.bin

Now it’s time to build a PEM file.

Building the PEM

Here, we will use openssl commands and a ASN.1 definition file with our computed values inserted in.

# Start with a SEQUENCE
asn1=SEQUENCE:pubkeyinfo

# pubkeyinfo contains an algorithm identifier and the public key wrapped
# in a BIT STRING
[pubkeyinfo]
algorithm=SEQUENCE:rsa_alg
pubkey=BITWRAP,SEQUENCE:rsapubkey

# algorithm ID for RSA is just an OID and a NULL
[rsa_alg]
algorithm=OID:rsaEncryption
parameter=NULL

# Actual public key: modulus and exponent
[rsapubkey] 
n=INTEGER:0xddb98664b412977fb0bc6655ad9788f871b3d178c1e85d65730cb1191365e0637773ba872722b91224ea04d1ca090efa9e399b111bc8a61f0d96b1f81b997cb5651b079cf7bd060191e81cf9f2b4abbf90019309f46825c636e9a5e207a6f6259391b4e0235e773cb1038486d8a34f7442e05d86433952ad99dda3492572b353fcd47ee8966d534bfde34d93fd3d2718d2dd1f3a26f3cce14aee97697f3eb28ccb7614c2f032d2d835c67641a051a859ed359e5b883892b24eb43397ff0f0c0000a9c3319e694f4a83785328f0941f846321afc6b8ea83575db4662d7194132b5b89eb103d56933290e45aacb1abfa4e1619f612bf7d8baf7c612e8b36e669

e=INTEGER:0x010001

First of all place, the modulus hex (generated above) code right after n=INTEGER:0xsame for the exponent hex code (also generated above)

Now it's possible to generate a DER File (BInary version of the certificate).

openssl asn1parse -genconf def.asn1 -out pubkey.der -noout

When we got the DER-File you will be able to convert it to a PEM (Texfile version from the certificate).

openssl rsa -in pubkey.der -inform der -pubin -out pubkey.pem

Finally, you can check that you got a 2048 bits public key with this command:

openssl rsa -pubin -in pubkey.pem -text -noout

In the next step, let's build a public key.

Building the SSH public key

Also, you might need to convert that PEM file to the SSH public key format:

ssh-keygen -i -m PKCS8 -f pubkey.pem

Here it is:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAADduZlktBKXf7C8ZlWtl4j4cbPReMHoXWVzDLEZE2XgY3dzuocnIrkSJOoE0coJDvqeOZsRG8imHw2WsfgbmXy1ZRsHnPe9BgGR6Bz58rSrf5ABkwn0aCXGNuml4gem9iWTkbTgI153PLEDhIbYo090QuBdhkM5Uq2Z3aNJJXKzU/zUfuiWbVNL/eNNk/09JxjS3R86JvPM4Urul2l/PrgMy3YUwvAy0tg1xnZBoFGoWe01nluIOJKyTrQzl/8PDAAAqcMxnmlPSoN4UyjwlB+EYyGvxrjqg1ddtGYtcZQTK1uJ64A9VpMykORarLGr/E4WGfYSv32Lr3xhLos25mk=

Et voila you got the public key for your azure B2C Tenant. You can use that to add this into every System that will verify every SAML or JWT Token against this key.

Tags