
// 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 java.security.interfaces.RSAPublicKey;
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;
import io.jsonwebtoken.security.Jwk;
import io.jsonwebtoken.security.Jwks;
// 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 IamSigningKeyResolver extends SigningKeyResolverAdapter
{
   private static final Logger LOGGER = Logger.getLogger(IamSigningKeyResolver.class.getName());
   private String keysURL;


   public IamSigningKeyResolver(String keysUrl) {this.keysURL = keysUrl;}

   @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;
   }



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

         String jsonKeys = doHttps();

         PublicKey pubKey = (PublicKey)getKeyFromKeys(jsonKeys, keyId);

         return pubKey;
      }


   private String doHttps() throws Exception
   {
      LOGGER.fine("trace 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;
   }


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

         Key key = null;

         ObjectMapper mapper = new ObjectMapper();

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

               LOGGER.finest("key: " + nodeContent);

               Jwk<?> jwk = Jwks.parser().build().parse(nodeContent);

               String jwkkid = jwk.getId();

               LOGGER.finest("kid-token : " + keyId + "kid-store : " + jwkkid + " key-type: " + jwk.getType());

               if(keyId.equals(jwkkid))
               {
                  key = jwk.toKey();
               }
            }
         }

         return key;
      }

}

