
// 1. HTTPS
import java.net.URL;
import java.io.*;
import javax.net.ssl.HttpsURLConnection;

// 2. json deser
//import org.codehaus.jackson.map.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonAutoDetect;


// 3, extract PublicKey
import java.util.Base64;
import java.io.ByteArrayInputStream;
import java.security.GeneralSecurityException; 
import java.security.PublicKey; 
import java.security.Signature; 
import java.security.cert.CertificateFactory; 
import java.security.cert.X509Certificate; 

// 4, validate token
import java.security.spec.InvalidKeySpecException;
import java.security.NoSuchAlgorithmException;
import java.security.Key;
import java.security.PublicKey;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.jackson.io.JacksonDeserializer;
import io.jsonwebtoken.SigningKeyResolverAdapter;

// only dbg: when keys taken from file, not URL
import java.nio.file.Files;
import java.nio.file.Paths;

import java.util.logging.Logger;

public class NeaSigningKeyResolver extends SigningKeyResolverAdapter
{
   private static final Logger LOGGER = Logger.getLogger(NeaSigningKeyResolver.class.getName());

   // FIXME to config file
   //final String pubkeyURL = "https://sso.neanias.eu/auth/realms/neanias-development";
   //final String keysURL = pubkeyURL + "/protocol/openid-connect/certs";
   // OR:
   // final String realmName = "skao-devel"; --> url= .../realms/ + realm + /protocol/openid-connect/...
   String pubkeyURL = "https://sso.neanias.eu/auth/realms/neanias-production";
   String keysURL = pubkeyURL + "/protocol/openid-connect/certs";
   // from ESc email: https://sso.neanias.eu/auth/realms/neanias-production/protocol/openid-connect/auth

   NeaSigningKeyResolver(String keysUrl) {this.keysURL = keysUrl;}

   private String doHttps() throws Exception
   {
      LOGGER.fine("keysURL : " + keysURL);

      URL myUrl = new URL(keysURL);
      HttpsURLConnection conn = (HttpsURLConnection)myUrl.openConnection();
      InputStream is = conn.getInputStream();
      InputStreamReader isr = new InputStreamReader(is);
      BufferedReader br = new BufferedReader(isr);

      String inputLine;
      String jsonKeys = ""; 
      while ((inputLine = br.readLine()) != null) {
         jsonKeys = jsonKeys + inputLine;
      }

      br.close();

      return jsonKeys;
   }




   // deserialize keys

   @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY)
   static class NeaKey 
   {
      String kid;
      String kty;
      String alg;
      String use;
      String n;
      String e;
      String[] x5c;
      String x5t;
      @JsonProperty("x5t#S256") String x5t_S256;
      }


   private String getCertFromKeys(String jsonKeys, String keyId)
         throws JsonProcessingException, GeneralSecurityException, IOException
      {
         LOGGER.fine( "trace");

         ObjectMapper mapper = new ObjectMapper();

         String cert = null;

         JsonNode keysNode = mapper.readTree(jsonKeys).get("keys");
         if(keysNode.isArray())
         {
            for (JsonNode node : keysNode)
            {
               String nodeContent = mapper.writeValueAsString(node);
               NeaKey key = mapper.readValue(nodeContent,NeaKey.class);

               LOGGER.finest("keyId    : " + keyId
                     +"\nKey::kid : " + key.kid);

               if(keyId.equals(key.kid))
               {
                  cert = key.x5c[0];
               }
            }
         }

         return cert;
      }




   private PublicKey getPublicKeyFromPemCert(String certBase64)
         throws GeneralSecurityException
      {
         LOGGER.fine( "trace" );

         CertificateFactory fac = CertificateFactory.getInstance("X509");
         ByteArrayInputStream in = new ByteArrayInputStream(Base64.getDecoder().decode(certBase64));
         X509Certificate cert = (X509Certificate)fac.generateCertificate(in);
         return cert.getPublicKey();
      }




   private Key lookupVerificationKey(String keyId)
         throws Exception, GeneralSecurityException
      {
         LOGGER.fine( "trace" );

         String jsonKeys = doHttps();

         String cert = getCertFromKeys(jsonKeys, keyId);

         PublicKey pubKey = (PublicKey)getPublicKeyFromPemCert(cert);

         return pubKey;
      }




   @Override
   public Key resolveSigningKey(JwsHeader jwsHeader, Claims claims)
   {
      LOGGER.fine( "trace" );

      //inspect the header or claims, lookup and return the signing key

      String keyId = jwsHeader.getKeyId(); //or any other field that you need to inspect

      Key key = null;
      try
      {
         key = lookupVerificationKey(keyId); //implement me
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }

      return key;
   }
}

