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