Help with connecting to WeatherKit REST API (Java)

I've been working on this for weeks to no avail; the best I've gotten so far is a 403 error, and I haven't been able to track down a fully-fleshed example in either Java or Kotlin.

First, after generating privateKey, here's my code to create a JWTS token. It seems to create one, but can someone please check if there's anything missing or wrong or unneccessary?

token = Jwts.builder()
        .setSubject    ("com.mycompany.myproject")
        .setId         ("QXXXXXXXXX.com.mycompany.myproject")
        .setHeaderParam("alg", "ES256")
        .setHeaderParam("kid", "CXXXXXXXXX")
        .setHeaderParam("id", "QXXXXXXXXX.com.mycompany.myproject")
        .setIssuedAt   (dateNow)
        .setExpiration (dateExpires)
        .claim("iss", "QXXXXXXXXX")
        .claim("iat", timeNow)      // same as dateNow, but in ms
        .claim("exp", timeExpires)  // same as dateExpires, but in ms
        .claim("sub", "com.mycompany.myproject")
        .signWith      (privateKey, SignatureAlgorithm.ES256)
        .compact();

With that in place, I'm creating the connection as follows. Although I've called URLs before, this is the first time I've attempted an authenticated connection, and there are a lot of moving parts.

URL url                = new URL("https://weatherkit.apple.com/api/v1/weather/en/40.5/-73.5?dataSets=currentWeather&timezone=Europe/London");

HttpURLConnection conn = null;
conn = (HttpURLConnection) url.openConnection()
conn.setRequestMethod   ("GET");
conn.setRequestProperty ("Authorization", "Bearer "+ token);
conn.setRequestProperty ("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty ("Content-Language", "en-US");
conn.setUseCaches       (false);
conn.setDoInput         (true);
conn.setDoOutput        (true);
conn.setConnectTimeout  (30000); //set timeout to 30 seconds

Executing conn.connect() churns for a little while and then returns a 403 error.

Any help from someone who's done this successfully is greatly appreciated.

Per a suggestion, I decoded the resulting token using https://jwt.io/ and received the following:

HEADER: { "alg": "ES256", "kid": "CXXXXXXXXX", "id": "QXXXXXXXXX.com.mycompany.myproject" }

PAYLOAD: { "sub": "com.mycompany.myproject", "jti": "QXXXXXXXXX.com.mycompany.myproject", "iat": 1658508182203, "exp": 1658508212203, "iss": "QXXXXXXXXX" }

VERIFY SIGNATURE: Invalid signature

(Note, in both this and the original post, I've obfuscated the Apple-issued Team ID and Service ID)

In light of the "Invalid signature" result from jwt.io as mentioned above, here is how I'm generating the private key (the .p8 file is in an /assets directory, with its headers stripped)

AssetManager assetManager   = this.getAssets();
InputStream stream          = assetManager.open("PRIVATE_KEY_FILE.p8");

byte[] bytes                = new byte[stream.available()];
int bytesRead               = stream.read(bytes);    // equals 200

byte[] pkcs8EncodedKey      = new byte[0];
pkcs8EncodedKey             = Base64.getDecoder().decode(bytes);

KeyFactory factory          = KeyFactory.getInstance("EC");
PrivateKey privateKey       = factory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey));
Help with connecting to WeatherKit REST API (Java)
 
 
Q