Create private key and certificate
- openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
- openssl req -new -key private_key.pem -out request.csr
- openssl x509 -req -days 365 -in request.csr -signkey private_key.pem -out certificate.pem
After this, the request.csr can be deleted. We now have the private key for signing a request JWT, and the certificate we need to register in EntraID and use for creating a x5t thumbprint.
Requesting a access token with certificate is done by creating another request JWT locally, sign it with a private key whos public certificate was registered on an application in EntraID. In Java, we do it like so:
In this example, we use library:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
//Read the X.509 certificate
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
final X509Certificate certificate = (X509Certificate) cf.generateCertificate(inputStreamFromCertificatePem);
//Read the private key for signing the JWT
final String privateKeyString = privateKeyStringFromPem
.replace("-----BEGIN PRIVATE KEY-----", "")
.replaceAll(System.lineSeparator(), "")
.replace("-----END PRIVATE KEY-----", "");
final byte[] encodedPrivateKey = Base64.getDecoder().decode(privateKeyString);
final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
final RSAPrivateKey privateKey (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
//Calculate the x5t thumbprint from the certificate
final byte[] derEncodedCert = certificate.getEncoded();
final MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
final byte[] certHash = sha1.digest(derEncodedCert);
final String x5tThumbPrint = Base64.getUrlEncoder().withoutPadding().encodeToString(certHash);
//Create the algorithm used by the JWT signing process
final Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) certificate.getPublicKey(), privateKey);
//Create the request JWT
final String requestJwt = JWT.create()
.withJWTId(UUID.randomUUID().toString())
.withHeader(Map.of("x5t", x5tThumbPrint))
.withAudience(audience)
.withExpiresAt(Instant.now().plusSeconds(tokenLifetimeInSeconds))
.withNotBefore(Instant.now())
.withSubject(clientId) //Client id
.withIssuer(clientId) //Client id
.withIssuedAt(Instant.now())
.sign(algorithm);
//Call the authentication endpoint
//E.g. https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
//Using Spring RestTemplate
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
final MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("scope", scope);
map.add("client_id", clientId);
map.add("grant_type", "client_credentials");
map.add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
map.add("client_assertion", requestJwt);
final HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(map, headers);
final ResponseEntity<JsonNode> restResponse = restTemplate.exchange(oauth2LogonUrl, HttpMethod.POST, httpEntity, JsonNode.class, tenantId);
//Normal response JSON from auth server. Get access_token etc from it.