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