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 }