001package com.nimbusds.jose.jwk;
002
003
004import java.text.ParseException;
005
006import net.minidev.json.JSONAware;
007import net.minidev.json.JSONObject;
008
009import com.nimbusds.jose.Algorithm;
010import com.nimbusds.jose.util.JSONObjectUtils;
011
012
013/**
014 * The base abstract class for JSON Web Keys (JWKs). It serialises to a JSON
015 * object.
016 *
017 * <p>The following JSON object members are common to all JWK types:
018 *
019 * <ul>
020 *     <li>{@link #getKeyType kty} (required)
021 *     <li>{@link #getKeyUse use} (optional)
022 *     <li>{@link #getKeyID kid} (optional)
023 * </ul>
024 *
025 * <p>Example JWK (of the Elliptic Curve type):
026 *
027 * <pre>
028 * {
029 *   "kty" : "EC",
030 *   "crv" : "P-256",
031 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
032 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
033 *   "use" : "enc",
034 *   "kid" : "1"
035 * }
036 * </pre>
037 *
038 * @author Vladimir Dzhuvinov
039 * @author Justin Richer
040 * @version $version$ (2013-03-19)
041 */
042public abstract class JWK implements JSONAware {
043
044
045        /**
046         * The key type, required.
047         */
048        private final KeyType kty;
049
050
051        /**
052         * The key use, optional.
053         */
054        private final Use use;
055
056
057        /**
058         * The intended JOSE algorithm for the key, optional.
059         */
060        private final Algorithm alg;
061
062
063        /**
064         * The key ID, optional.
065         */
066        private final String kid;
067
068
069        /**
070         * Creates a new JSON Web Key (JWK).
071         *
072         * @param kty The key type. Must not be {@code null}.
073         * @param use The key use, {@code null} if not specified or if the key 
074         *            is intended for signing as well as encryption.
075         * @param alg The intended JOSE algorithm for the key, {@code null} if
076         *            not specified.
077         * @param kid The key ID, {@code null} if not specified.
078         */
079        public JWK(final KeyType kty, final Use use, final Algorithm alg, final String kid) {
080
081                if (kty == null) {
082                        throw new IllegalArgumentException("The key type \"kty\" must not be null");
083                }
084
085                this.kty = kty;
086
087                this.use = use;
088
089                this.alg = alg;
090
091                this.kid = kid;
092        }
093
094
095        /**
096         * Gets the type ({@code kty}) of this JWK.
097         *
098         * @return The key type.
099         */
100        public KeyType getKeyType() {
101
102                return kty;
103        }
104
105
106        /**
107         * Gets the use ({@code use}) of this JWK.
108         *
109         * @return The key use, {@code null} if not specified or if the key is
110         *         intended for signing as well as encryption.
111         */
112        public Use getKeyUse() {
113
114                return use;
115        }
116
117
118        /**
119         * Gets the intended JOSE algorithm ({@code alg}) for this JWK.
120         *
121         * @return The intended JOSE algorithm, {@code null} if not specified.
122         */
123        public Algorithm getAlgorithm() {
124
125                return alg;
126        }
127
128
129        /**
130         * Gets the ID ({@code kid}) of this JWK. The key ID can be used to 
131         * match a specific key. This can be used, for instance, to choose a 
132         * key within a {@link JWKSet} during key rollover. The key ID may also 
133         * correspond to a JWS/JWE {@code kid} header parameter value.
134         *
135         * @return The key ID, {@code null} if not specified.
136         */
137        public String getKeyID() {
138
139                return kid;
140        }
141
142
143        /**
144         * Returns {@code true} if this JWK contains private or sensitive
145         * (non-public) parameters.
146         *
147         * @return {@code true} if this JWK contains private parameters, else
148         *         {@code false}.
149         */
150        public abstract boolean isPrivate();
151
152
153        /**
154         * Creates a copy of this JWK with all private or sensitive parameters 
155         * removed.
156         * 
157         * @return The newly created public JWK, or {@code null} if none can be
158         *         created.
159         */
160        public abstract JWK toPublicJWK();
161
162
163        /**
164         * Returns a JSON object representation of this JWK. This method is 
165         * intended to be called from extending classes.
166         *
167         * <p>Example:
168         *
169         * <pre>
170         * {
171         *   "kty" : "RSA",
172         *   "use" : "sig",
173         *   "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b"
174         * }
175         * </pre>
176         *
177         * @return The JSON object representation.
178         */
179        public JSONObject toJSONObject() {
180
181                JSONObject o = new JSONObject();
182
183                o.put("kty", kty.getValue());
184
185                if (use != null) {
186
187                        if (use == Use.SIGNATURE) {
188                                o.put("use", "sig");
189                        }
190
191                        if (use == Use.ENCRYPTION) {
192                                o.put("use", "enc");
193                        }
194                }
195
196                if (alg != null) {
197                        o.put("alg", alg.getName());
198                }
199
200                if (kid != null) {
201                        o.put("kid", kid);
202                }
203
204                return o;
205        }
206
207
208        /**
209         * Returns the JSON object string representation of this JWK.
210         *
211         * @return The JSON object string representation.
212         */
213        @Override
214        public String toJSONString() {
215
216                return toJSONObject().toString();
217        }
218
219
220        /**
221         * @see #toJSONString
222         */
223        @Override
224        public String toString() {
225
226                return toJSONObject().toString();
227        }
228
229
230        /**
231         * Parses a JWK from the specified JSON object string representation. 
232         * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 
233         * {@link OctetSequenceKey}.
234         *
235         * @param s The JSON object string to parse. Must not be {@code null}.
236         *
237         * @return The JWK.
238         *
239         * @throws ParseException If the string couldn't be parsed to a
240         *                        supported JWK.
241         */
242        public static JWK parse(final String s)
243                throws ParseException {
244
245                return parse(JSONObjectUtils.parseJSONObject(s));
246        }
247
248
249        /**
250         * Parses a JWK from the specified JSON object representation. The JWK 
251         * must be an {@link ECKey}, an {@link RSAKey}, or a 
252         * {@link OctetSequenceKey}.
253         *
254         * @param jsonObject The JSON object to parse. Must not be 
255         *                   {@code null}.
256         *
257         * @return The JWK.
258         *
259         * @throws ParseException If the JSON object couldn't be parsed to a 
260         *                        supported JWK.
261         */
262        public static JWK parse(final JSONObject jsonObject)
263                throws ParseException {
264
265                KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
266
267                if (kty == KeyType.EC) {
268                        
269                        return ECKey.parse(jsonObject);
270
271                } else if (kty == KeyType.RSA) {
272                        
273                        return RSAKey.parse(jsonObject);
274
275                } else if (kty == KeyType.OCT) {
276                        
277                        return OctetSequenceKey.parse(jsonObject);
278
279                } else {
280
281                        throw new ParseException("Unsupported key type \"kty\" parameter: " + kty, 0);
282                }
283        }
284
285
286        /**
287         * Parses a key use ({@code use}) parameter from the specified JSON 
288         * object representation of a JWK.
289         *
290         * @param jsonObject The JSON object to parse. Must not be 
291         *                   {@code null}.
292         *
293         * @return The key use, {@code null} if not specified.
294         *
295         * @throws ParseException If the key use parameter couldn't be parsed.
296         */
297        protected static Use parseKeyUse(final JSONObject jsonObject)
298                throws ParseException {
299
300                if (jsonObject.get("use") == null) {
301                        return null;
302                }
303
304                String useStr = JSONObjectUtils.getString(jsonObject, "use");
305
306                if (useStr.equals("sig")) {
307
308                        return Use.SIGNATURE;
309
310                } else if (useStr.equals("enc")) {
311
312                        return Use.ENCRYPTION;
313
314                } else {
315                        
316                        throw new ParseException("Invalid or unsupported key use \"use\" parameter, must be \"sig\" or \"enc\"", 0);
317                }
318        }
319
320
321        /**
322         * Parses an algorithm ({@code alg}) parameter from the specified JSON
323         * object representation of a JWK.
324         *
325         * <p>Note that the algorithm requirement level is not inferred.
326         *
327         * @param jsonObject The JSON object to parse. Must not be 
328         *                   {@code null}.
329         *
330         * @return The algorithm, {@code null} if not specified.
331         *
332         * @throws ParseException If the algorithm parameter couldn't be
333         *                        parsed.
334         */
335        protected static Algorithm parseAlgorithm(final JSONObject jsonObject)
336                throws ParseException {
337
338                if (jsonObject.get("alg") == null) {
339
340                        return null;
341                }
342
343                String algStr = JSONObjectUtils.getString(jsonObject, "alg");
344
345                return new Algorithm(algStr);
346        }
347
348
349        /**
350         * Parses a key ID ({@code kid}) parameter from the specified JSON
351         * object representation of a JWK.
352         *
353         * @param jsonObject The JSON object to parse. Must not be 
354         *                   {@code null}.
355         *
356         * @return The key ID, {@code null} if not specified.
357         *
358         * @throws ParseException If the key ID parameter couldn't be parsed.
359         */
360        protected static String parseKeyID(final JSONObject jsonObject)
361                throws ParseException {
362
363                if (jsonObject.get("kid") == null) {
364
365                        return null;
366                }
367
368                return JSONObjectUtils.getString(jsonObject, "kid");
369        }
370}