View Javadoc
1   /*
2   * The contents of this file are subject to the terms of the Common Development and
3   * Distribution License (the License). You may not use this file except in compliance with the
4   * License.
5   *
6   * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7   * specific language governing permission and limitations under the License.
8   *
9   * When distributing Covered Software, include this CDDL Header Notice in each file and include
10  * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11  * Header, with the fields enclosed by brackets [] replaced by your own identifying
12  * information: "Portions copyright [year] [name of copyright owner]".
13  *
14  * Copyright 2014-2017 ForgeRock AS.
15  */
16  
17  package org.forgerock.json.jose.jwk;
18  
19  import java.io.IOException;
20  import java.net.URL;
21  import java.security.Key;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import org.forgerock.json.jose.exceptions.FailedToLoadJWKException;
26  import org.forgerock.util.SimpleHTTPClient;
27  
28  /**
29   * Provides methods to gather a JWKSet from a URL and return
30   * a map of key ids to keys as dictated by that JWKS.
31   */
32  public class JWKSetParser {
33  
34      private SimpleHTTPClient simpleHTTPClient;
35      private JWKLookup jwkLookup;
36  
37      /**
38       * Constructor allowing the configuration of the read and connection timeouts used
39       * by the HTTP client for this parser.
40       *
41       * @param readTimeout read timeout in ms
42       * @param connTimeout connection timeout in ms
43       */
44      public JWKSetParser(final int readTimeout, final int connTimeout) {
45          this(new SimpleHTTPClient(readTimeout, connTimeout));
46      }
47  
48      /**
49       * Alternative constructor allowing the calling class to pass in an
50       * already-configured {@link SimpleHTTPClient}.
51       *
52       * @param simpleHTTPClient {@link SimpleHTTPClient} used to gather HTTP information
53       */
54      public JWKSetParser(final SimpleHTTPClient simpleHTTPClient) {
55          this(simpleHTTPClient, new JWKLookup());
56      }
57  
58      /**
59       * Alternative constructor allowing the calling class to pass in an
60       * already-configured {@link SimpleHTTPClient}.
61       *
62       * @param simpleHTTPClient {@link SimpleHTTPClient} used to gather HTTP information
63       * @param jwkLookup to convert the jwk into a real key
64       */
65      public JWKSetParser(final SimpleHTTPClient simpleHTTPClient, final JWKLookup jwkLookup) {
66          this.simpleHTTPClient = simpleHTTPClient;
67          this.jwkLookup = jwkLookup;
68      }
69  
70      /**
71       * Provides a Map of KeyId:Keys as indicated by the JWKSet's URL.
72       *
73       * @param url The URL from which to gather the JWKSet
74       * @return a map of currently valid KeyId:Keys for the provider associated with this URL
75       * @throws FailedToLoadJWKException If there are problems connecting to or parsing the response
76       */
77      public Map<String, Key> generateMapFromJWK(URL url) throws FailedToLoadJWKException {
78          return jwkSetToMap(jwkSet(url));
79      }
80  
81      /**
82       * Uses the SimpleHTTPClient to gather HTTP information.
83       *
84       * @param url The URL from which to read the information
85       * @return a String containing the returned JSON
86       * @throws FailedToLoadJWKException If there are problems connecting to the URL
87       */
88      private String gatherHttpContents(URL url) throws FailedToLoadJWKException {
89          try {
90              return simpleHTTPClient.get(url);
91          } catch (IOException e) {
92              throw new FailedToLoadJWKException("Unable to load the JWK location over HTTP", e);
93          }
94      }
95  
96      /**
97       * Provides a jwks set as indicated by the JWKSet's URL.
98       *
99       * @param url The URL from which to gather the JWKSet
100      * @return a jwks set valid for the provider associated with this URL
101      * @throws FailedToLoadJWKException If there are problems connecting to or parsing the response
102      */
103     public JWKSet jwkSet(URL url) throws FailedToLoadJWKException {
104         final String jwksContents = gatherHttpContents(url);
105         return JWKSet.parse(jwksContents);
106     }
107 
108     /**
109      * Converts a supplied JWKSet into a map of key:values, where the keys are the keyIds and the
110      * values are verification keys.
111      *
112      * @param jwkSet The JWKSet to convert
113      * @return A map of key ids to their respective keys
114      * @throws FailedToLoadJWKException If there are issues parsing the JWKSet's contents
115      */
116     public Map<String, Key> jwkSetToMap(JWKSet jwkSet) throws FailedToLoadJWKException {
117 
118         final Map<String, Key> keyMap = new HashMap<>();
119 
120         //store the retrieved JSON as String (kid) : Key (having converted) in this resolver
121         for (JWK jwk : jwkSet.getJWKsAsList()) {
122             final Key key = jwkLookup.lookup(jwk.toJsonString(), jwk.getKeyType());
123             keyMap.put(jwk.getKeyId(), key);
124         }
125         return keyMap;
126     }
127 }