Search code examples
jwknimbus-jose-jwt

How to include x5t and x5c in JWK output?


I am trying to call some APIs published by the IRS. They require me to create a JWK and send them the public key.

I was able to generate an RSA JWK key using this code:

package com.propfinancing.jwk;

import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import java.util.UUID;

public class GenerateKey {
  public static void main(String[] args) 
  throws Exception {
    RSAKey jwk = new RSAKeyGenerator(2048)
        .keyUse(KeyUse.SIGNATURE)
        .keyID(UUID.randomUUID().toString())
        .generate();  
    
    System.out.println(jwk);

    System.out.println(jwk.toPublicJWK());
  }
}

Here is a sample output:

{"p":"0nDimdByKTsWOOHWHHkOpdNgIgG5vVmd_Bdc1wQG5lCz1Gh_Iu4z0KaBeAlsoqkak3B2Hv--62nBBIdsqWLOZ4iwed_uIkUItZQNADltG_gEfNAVpY0ESN43ZDykBVOw28FVEiN8ewxZ5rmqKidOgEgMEMbHJ45gqfHm9XD2GLM","kty":"RSA","q":"-TFekmWY2i1_U9K3bDBzJL-cFZfB5JX1qMUiSQhoB_TNymY73UlwZsMap732Jb4v5ZDxIZIjBcHAhRwsEZEEkWyF6mxnUifdbAK4OxaKlw7q1EvUUUzMGqegek-d4ZLwxBD54UgdvTbWxUDlgLlXispfJdr0RwEtOKQzZ53vFU8","d":"DvhjUHjNTZfEeFBAajfZvkznePJmYvXn6cPaclNp3OmMuqYgDSWSciVrP5fyieRX6TgYbaClzoP_UUsXqaTdd1WVySyZEEDh4JGja6n0Y7KwD2DAgEgLhSHwznCp-YV4vtfrLOBpFAwem8S2FaC8vujFzvskiJ3yWfidp-qZGJfGo2wdh2Ry8vkgBRJdEjyVvhKYVR2_UeS3otXQwaLgHHy-Cl8ukiWtXNT5Z5I7CvQOSEy3QpzVUWEnPod6g6j0sfhLMfAkj1i7RfDaJB-8SzlwR5DEhvRrgWJxJw3LWkOcHBAlfepDV63eilM5msDkhlcu_gmzmX-WksyvClgSiw","e":"AQAB","use":"sig","kid":"1ca36b31-c202-4242-8e36-80bb06d2219a","qi":"VvLjYgSBL35kShVGEC2j8BiSAUsyI0QL7aZDdYY6gA-Ba2zSsQQJHg6OISr6rHHJKHopCs1i2PPRd38c7apO8gLwjbk8KsyhYRl864BKfdHfcQo4r3XOH2tDZmsNuc3fpVARKACcQgwtrhGMcIr0M9keghv9wuui7ZQnFPuu0Zk","dp":"n9kZdwbLwJ_eOGTrE4Os6a8OxaYT5U2OYK-KWNT5PLcucfjSIcFYYHQpwfk-qKvUwQva3Z8b8YvKDtujYWLpL-G3U0wSgbt4axzDZ1k3lNgm6HvTBYaBK6yH3L7nRBiXLcXEDdwgsQLnwwdix1RGH9EaQSAMpPJUYKKb8fHVLVs","dq":"MtdCv0WhMOpVbwmvyI_9_gJl2vXyZiu-SiemPhZL-0uT7PZ8wkIof57QwmZ8YOjzpreyqrHBGXyVRdMgnpjiwLTOgayKS_W8NDw90DhiID235YZvFANCJTIMCMhTouEy5B2-jZsEDkWw_d-ms2OdG8D8NqH8crwnuMWvgzVywPs","n":"zNhhwMZJ1f3rNlE62Nyj_lL_ANJYNlIzImH8Uk5QkSO2Wy2oP8yqfcLfvlHyQTP4R5-p7ibT_hC4lEr9BPjseX7Dghd6NMCkRtlhsk2qg7SfSQecNczWZgH5cisMtE_DFWONVpgQRfoaIZV3PSoPAClNTXk52Ni9NaK1mVXsBRP0RD_Hx0r8G8De5UQ8BRNcqYF9hhWzEv1RFLJqZTHMAKeSTmiGJ5Dyur0Wv4kxY73iZtM5ld6f21Q-w9bJ0ar1DVFkmsNtt4Ed20zkj7NOKhBnGqI2VbxQ6WQ7T4Ik6ATf6ujBxLDepme153eH63-1IutZ-Waj8Qfjn2Pt4hVOPQ"}
{"kty":"RSA","e":"AQAB","use":"sig","kid":"1ca36b31-c202-4242-8e36-80bb06d2219a","n":"zNhhwMZJ1f3rNlE62Nyj_lL_ANJYNlIzImH8Uk5QkSO2Wy2oP8yqfcLfvlHyQTP4R5-p7ibT_hC4lEr9BPjseX7Dghd6NMCkRtlhsk2qg7SfSQecNczWZgH5cisMtE_DFWONVpgQRfoaIZV3PSoPAClNTXk52Ni9NaK1mVXsBRP0RD_Hx0r8G8De5UQ8BRNcqYF9hhWzEv1RFLJqZTHMAKeSTmiGJ5Dyur0Wv4kxY73iZtM5ld6f21Q-w9bJ0ar1DVFkmsNtt4Ed20zkj7NOKhBnGqI2VbxQ6WQ7T4Ik6ATf6ujBxLDepme153eH63-1IutZ-Waj8Qfjn2Pt4hVOPQ"}

Everything looks good on the output, but the IRS requires the JWK to include x5t and x5c parameters in the output.

How do I add those?

Thanks, Neil


Solution

  • I was able to figure out how to generate the self-signed certificate using nimbus-jose-jwt.

    Here is my sample code:

    package jwk;
    
    import com.nimbusds.jose.jwk.JWKSet;
    import com.nimbusds.jose.jwk.KeyUse;
    import com.nimbusds.jose.jwk.RSAKey;
    import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
    import com.nimbusds.jose.util.Base64;
    import com.nimbusds.oauth2.sdk.id.Issuer;
    import com.nimbusds.oauth2.sdk.util.X509CertificateUtils;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.FileWriter;
    import java.io.PrintWriter;
    import java.security.KeyStore;
    import java.security.cert.X509Certificate;
    import java.util.Calendar;
    import java.util.Collections;
    import java.util.Date;
    import java.util.GregorianCalendar;
    import java.util.UUID;
    
    public class JWKTest {
      public static void main(String[] args)
      throws Exception {
        // Create a JWK with a random ID
        RSAKey rsaJWK = new RSAKeyGenerator(2048)
            .keyID(UUID.randomUUID().toString())
            .keyUse(KeyUse.SIGNATURE)
            .generate();
    
        // Create the cert start and expiration dates
        Calendar now = new GregorianCalendar();
        now.add(Calendar.DAY_OF_YEAR, -1);
        Date certStartDate = now.getTime();
        now.add(Calendar.DAY_OF_YEAR, 1);
        now.add(Calendar.YEAR, 10);
        Date certExpirationDate = now.getTime();
    
        // Generate a self signed certificate
        X509Certificate cert = X509CertificateUtils.generateSelfSigned(
            new Issuer("My issuer"),
            certStartDate,
            certExpirationDate,
            rsaJWK.toRSAPublicKey(),
            rsaJWK.toRSAPrivateKey());
    
        // Create a key store to hold the certificate
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null);
        keyStore.setCertificateEntry("My Cert", cert);
        FileOutputStream outStream = new FileOutputStream(new File("C://Tmp//key.store"));
        keyStore.store(outStream, null);
        outStream.close();
    
        // Create private key from the certificate
        RSAKey privateJWK = new RSAKey.Builder(rsaJWK)
            .x509CertChain(Collections.singletonList(Base64.encode(cert.getEncoded())))
            .x509CertThumbprint(rsaJWK.computeThumbprint())
            .build();
        PrintWriter writer = new PrintWriter(new FileWriter("C://Tmp//private.jwk"));
        writer.println(privateJWK);
        writer.close();
        
        // Create a public key
        JWKSet publicJWK = new JWKSet(rsaJWK);
        writer = new PrintWriter(new FileWriter("C://Tmp//public.jwk"));
        writer.println(publicJWK);
        writer.close();
      }
    }