001 package com.nimbusds.jose;
002
003
004 import java.text.ParseException;
005
006 import java.util.HashMap;
007 import java.util.LinkedList;
008 import java.util.List;
009 import java.util.Map;
010
011 import net.minidev.json.JSONArray;
012 import net.minidev.json.JSONObject;
013
014 import com.nimbusds.jose.util.JSONObjectUtils;
015
016
017 /**
018 * JSON Web Key (JWK) set. Represented by a JSON object that contains an array
019 * of {@link JWK JSON Web Keys} (JWKs) as the value of its "keys" member.
020 * Additional (custom) members of the JWK Set JSON object are also supported.
021 *
022 * <p>Example JSON Web Key (JWK) set:
023 *
024 * <pre>
025 * {
026 * "keys" : [ { "kty" : "EC",
027 * "crv" : "P-256",
028 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
029 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
030 * "use" : "enc",
031 * "kid" : "1" },
032 *
033 * { "kty" : "RSA",
034 * "n" : "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx
035 * 4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs
036 * tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2
037 * QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI
038 * SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb
039 * w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
040 * "e" : "AQAB",
041 * "alg" : "RS256",
042 * "kid" : "2011-04-29" } ]
043 * }
044 * </pre>
045 *
046 * @author Vladimir Dzhuvinov
047 * @version $version$ (2013-01-08)
048 */
049 public class JWKSet {
050
051
052 /**
053 * The JWK list.
054 */
055 private final List<JWK> keys = new LinkedList<JWK>();
056
057
058 /**
059 * Additional custom members.
060 */
061 private final Map<String,Object> customMembers = new HashMap<String,Object>();
062
063
064 /**
065 * Creates a new empty JSON Web Key (JWK) set.
066 */
067 public JWKSet() {
068
069 // Nothing to do
070 }
071
072
073 /**
074 * Creates a new JSON Web Key (JWK) set with a single key.
075 *
076 * @param key The JWK. Must not be {@code null}.
077 */
078 public JWKSet(final JWK key) {
079
080 if (key == null)
081 throw new IllegalArgumentException("The JWK must not be null");
082
083 keys.add(key);
084 }
085
086
087 /**
088 * Creates a new JSON Web Key (JWK) set with the specified keys.
089 *
090 * @param keys The JWK list. Must not be {@code null}.
091 */
092 public JWKSet(final List<JWK> keys) {
093
094 if (keys == null)
095 throw new IllegalArgumentException("The JWK list must not be null");
096
097 this.keys.addAll(keys);
098 }
099
100
101 /**
102 * Creates a new JSON Web Key (JWK) set with the specified keys and
103 * additional custom members.
104 *
105 * @param keys The JWK list. Must not be {@code null}.
106 * @param customMembers The additional custom members. Must not be
107 * {@code null}.
108 */
109 public JWKSet(final List<JWK> keys, final Map<String,Object> customMembers) {
110
111 if (keys == null)
112 throw new IllegalArgumentException("The JWK list must not be null");
113
114 this.keys.addAll(keys);
115
116 this.customMembers.putAll(customMembers);
117 }
118
119
120 /**
121 * Gets the keys (ordered) of this JSON Web Key (JWK) set.
122 *
123 * @return The keys, empty list if none.
124 */
125 public List<JWK> getKeys() {
126
127 return keys;
128 }
129
130
131 /**
132 * Gets the additional custom members of this JSON Web Key (JWK) set.
133 *
134 * @return The additional custom members, empty map if none.
135 */
136 public Map<String,Object> getAdditionalMembers() {
137
138 return customMembers;
139 }
140
141
142 /**
143 * Returns a JSON object representation of this JSON Web Key (JWK) set.
144 *
145 * @return The JSON object representation.
146 */
147 public JSONObject toJSONObject() {
148
149 JSONObject o = new JSONObject(customMembers);
150
151 JSONArray a = new JSONArray();
152
153 for (final JWK key: keys) {
154
155 a.add(key.toJSONObject());
156 }
157
158 o.put("keys", a);
159
160 return o;
161 }
162
163
164 /**
165 * Returns the JSON object string representation of this JSON Web Key
166 * (JWK) set.
167 *
168 * @return The JSON object string representation.
169 */
170 public String toString() {
171
172 return toJSONObject().toString();
173 }
174
175
176 /**
177 * Parses the specified string representing a JSON Web Key (JWK) set.
178 *
179 * @param s The string to parse. Must not be {@code null}.
180 *
181 * @return The JSON Web Key (JWK) set.
182 *
183 * @throws ParseException If the string couldn't be parsed to a valid
184 * JSON Web Key (JWK) set.
185 */
186 public static JWKSet parse(final String s)
187 throws ParseException {
188
189 return parse(JSONObjectUtils.parseJSONObject(s));
190 }
191
192
193 /**
194 * Parses the specified JSON object representing a JSON Web Key (JWK)
195 * set.
196 *
197 * @param json The JSON object to parse. Must not be {@code null}.
198 *
199 * @return The JSON Web Key (JWK) set.
200 *
201 * @throws ParseException If the string couldn't be parsed to a valid
202 * JSON Web Key (JWK) set.
203 */
204 public static JWKSet parse(final JSONObject json)
205 throws ParseException {
206
207 JSONArray keyArray = JSONObjectUtils.getJSONArray(json, "keys");
208
209 List<JWK> keys = new LinkedList<JWK>();
210
211 for (int i=0; i < keyArray.size(); i++) {
212
213 if (! (keyArray.get(i) instanceof JSONObject))
214 throw new ParseException("The \"keys\" JSON array must contain JSON objects only", 0);
215
216 JSONObject keyJSON = (JSONObject)keyArray.get(i);
217
218 try {
219 keys.add(JWK.parse(keyJSON));
220
221 } catch (ParseException e) {
222
223 throw new ParseException("Invalid JWK at position " + i + ": " + e.getMessage(), 0);
224 }
225 }
226
227 // Parse additional custom members
228 JWKSet jwkSet = new JWKSet(keys);
229
230 for (Map.Entry<String,Object> entry: json.entrySet()) {
231
232 if (entry.getKey() == null || entry.getKey().equals("keys"))
233 continue;
234
235 jwkSet.getAdditionalMembers().put(entry.getKey(), entry.getValue());
236 }
237
238 return jwkSet;
239 }
240 }