001package com.nimbusds.jose.jwk;
002
003
004import java.net.URL;
005import java.util.List;
006import java.text.ParseException;
007import java.util.Set;
008
009import net.jcip.annotations.Immutable;
010
011import net.minidev.json.JSONObject;
012
013import com.nimbusds.jose.Algorithm;
014import com.nimbusds.jose.util.Base64;
015import com.nimbusds.jose.util.Base64URL;
016import com.nimbusds.jose.util.JSONObjectUtils;
017import com.nimbusds.jose.util.X509CertChainUtils;
018
019
020/**
021 * {@link KeyType#OCT Octet sequence} JSON Web Key (JWK), used to represent
022 * symmetric keys. This class is immutable.
023 *
024 * <p>Example JSON object representation of an octet sequence JWK:
025 *
026 * <pre>
027 * {
028 *   "kty" : "oct",
029 *   "alg" : "A128KW",
030 *   "k"   : "GawgguFyGrWKav7AX4VKUg"
031 * }
032 * </pre>
033 * 
034 * @author Justin Richer
035 * @author Vladimir Dzhuvinov
036 * @version $version$ (2014-04-20)
037 */
038@Immutable
039public final class OctetSequenceKey extends JWK {
040
041
042        /**
043         * The symmetric key value.
044         */
045        private final Base64URL k;
046
047
048        /**
049         * Builder for constructing octet sequence JWKs.
050         *
051         * <p>Example use:
052         *
053         * <pre>
054         * OctetSequenceKey key = new OctetSequenceKey.Builder(k).
055         *                        algorithm(JWSAlgorithm.HS512).
056         *                        keyID("123").
057         *                        build();
058         * </pre>
059         */
060        public static class Builder {
061
062
063                /**
064                 * The symmetric key value.
065                 */
066                private final Base64URL k;
067
068
069                /**
070                 * The public key use, optional.
071                 */
072                private KeyUse use;
073
074
075                /**
076                 * The key operations, optional.
077                 */
078                private Set<KeyOperation> ops;
079
080
081                /**
082                 * The intended JOSE algorithm for the key, optional.
083                 */
084                private Algorithm alg;
085
086
087                /**
088                 * The key ID, optional.
089                 */
090                private String kid;
091
092
093                /**
094                 * X.509 certificate URL, optional.
095                 */
096                private URL x5u;
097
098
099                /**
100                 * X.509 certificate thumbprint, optional.
101                 */
102                private Base64URL x5t;
103
104
105                /**
106                 * The X.509 certificate chain, optional.
107                 */
108                private List<Base64> x5c;
109
110
111                /**
112                 * Creates a new octet sequence JWK builder.
113                 *
114                 * @param k The key value. It is represented as the Base64URL 
115                 *          encoding of value's big endian representation. Must
116                 *          not be {@code null}.
117                 */
118                public Builder(final Base64URL k) {
119
120                        if (k == null) {
121                                throw new IllegalArgumentException("The key value must not be null");
122                        }
123
124                        this.k = k;
125                }
126
127
128                /**
129                 * Sets the use ({@code use}) of the JWK.
130                 *
131                 * @param use The key use, {@code null} if not specified or if
132                 *            the key is intended for signing as well as
133                 *            encryption.
134                 *
135                 * @return This builder.
136                 */
137                public Builder keyUse(final KeyUse use) {
138
139                        this.use = use;
140                        return this;
141                }
142
143
144                /**
145                 * Sets the operations ({@code key_ops}) of the JWK (for a
146                 * non-public key).
147                 *
148                 * @param ops The key operations, {@code null} if not
149                 *            specified.
150                 *
151                 * @return This builder.
152                 */
153                public Builder keyOperations(final Set<KeyOperation> ops) {
154
155                        this.ops = ops;
156                        return this;
157                }
158
159
160                /**
161                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
162                 *
163                 * @param alg The intended JOSE algorithm, {@code null} if not 
164                 *            specified.
165                 *
166                 * @return This builder.
167                 */
168                public Builder algorithm(final Algorithm alg) {
169
170                        this.alg = alg;
171                        return this;
172                }
173
174                /**
175                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 
176                 * to match a specific key. This can be used, for instance, to 
177                 * choose a key within a {@link JWKSet} during key rollover. 
178                 * The key ID may also correspond to a JWS/JWE {@code kid} 
179                 * header parameter value.
180                 *
181                 * @param kid The key ID, {@code null} if not specified.
182                 *
183                 * @return This builder.
184                 */
185                public Builder keyID(final String kid) {
186
187                        this.kid = kid;
188                        return this;
189                }
190
191
192                /**
193                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
194                 *
195                 * @param x5u The X.509 certificate URL, {@code null} if not 
196                 *            specified.
197                 *
198                 * @return This builder.
199                 */
200                public Builder x509CertURL(final URL x5u) {
201
202                        this.x5u = x5u;
203                        return this;
204                }
205
206
207                /**
208                 * Sets the X.509 certificate thumbprint ({@code x5t}) of the
209                 * JWK.
210                 *
211                 * @param x5t The X.509 certificate thumbprint, {@code null} if 
212                 *            not specified.
213                 *
214                 * @return This builder.
215                 */
216                public Builder x509CertThumbprint(final Base64URL x5t) {
217
218                        this.x5t = x5t;
219                        return this;
220                }
221
222                /**
223                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
224                 *
225                 * @param x5c The X.509 certificate chain as a unmodifiable 
226                 *            list, {@code null} if not specified.
227                 *
228                 * @return This builder.
229                 */
230                public Builder x509CertChain(final List<Base64> x5c) {
231
232                        this.x5c = x5c;
233                        return this;
234                }
235
236                /**
237                 * Builds a new octet sequence JWK.
238                 *
239                 * @return The octet sequence JWK.
240                 *
241                 * @throws IllegalStateException If the JWK parameters were
242                 *                               inconsistently specified.
243                 */
244                public OctetSequenceKey build() {
245
246                        try {
247                                return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5c);
248
249                        } catch (IllegalArgumentException e) {
250
251                                throw new IllegalStateException(e.getMessage(), e);
252                        }
253                }
254        }
255
256        
257        /**
258         * Creates a new octet sequence JSON Web Key (JWK) with the specified
259         * parameters.
260         *
261         * @param k   The key value. It is represented as the Base64URL 
262         *            encoding of value's big endian representation. Must not 
263         *            be {@code null}.
264         * @param use The key use, {@code null} if not specified or if the key
265         *            is intended for signing as well as encryption.
266         * @param ops The key operations, {@code null} if not specified.
267         * @param alg The intended JOSE algorithm for the key, {@code null} if
268         *            not specified.
269         * @param kid The key ID. {@code null} if not specified.
270         * @param x5u The X.509 certificate URL, {@code null} if not specified.
271         * @param x5t The X.509 certificate thumbprint, {@code null} if not
272         *            specified.
273         * @param x5c The X.509 certificate chain, {@code null} if not 
274         *            specified.
275         */
276        public OctetSequenceKey(final Base64URL k,
277                                final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
278                                final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
279        
280                super(KeyType.OCT, use, ops, alg, kid, x5u, x5t, x5c);
281
282                if (k == null) {
283                        throw new IllegalArgumentException("The key value must not be null");
284                }
285
286                this.k = k;
287        }
288    
289
290        /**
291         * Returns the value of this octet sequence key. 
292         *
293         * @return The key value. It is represented as the Base64URL encoding 
294         *         of the coordinate's big endian representation.
295         */
296        public Base64URL getKeyValue() {
297
298                return k;
299        }
300        
301        
302        /**
303         * Returns a copy of this octet sequence key value as a byte array.
304         * 
305         * @return The key value as a byte array.
306         */
307        public byte[] toByteArray() {
308
309                return getKeyValue().decode();
310        }
311
312
313        /**
314         * Octet sequence (symmetric) keys are never considered public, this 
315         * method always returns {@code true}.
316         *
317         * @return {@code true}
318         */
319        @Override
320        public boolean isPrivate() {
321
322                return true;
323        }
324
325
326        /**
327         * Octet sequence (symmetric) keys are never considered public, this 
328         * method always returns {@code null}.
329         *
330         * @return {@code null}
331         */
332        @Override
333        public OctetSequenceKey toPublicJWK() {
334
335                return null;
336        }
337        
338
339        @Override
340        public JSONObject toJSONObject() {
341
342                JSONObject o = super.toJSONObject();
343
344                // Append key value
345                o.put("k", k.toString());
346                
347                return o;
348        }
349
350
351        /**
352         * Parses an octet sequence JWK from the specified JSON object string 
353         * representation.
354         *
355         * @param s The JSON object string to parse. Must not be {@code null}.
356         *
357         * @return The octet sequence JWK.
358         *
359         * @throws ParseException If the string couldn't be parsed to an octet
360         *                        sequence JWK.
361         */
362        public static OctetSequenceKey parse(final String s)
363                throws ParseException {
364
365                return parse(JSONObjectUtils.parseJSONObject(s));
366        }
367
368        
369        /**
370         * Parses an octet sequence JWK from the specified JSON object 
371         * representation.
372         *
373         * @param jsonObject The JSON object to parse. Must not be 
374         *                   @code null}.
375         *
376         * @return The octet sequence JWK.
377         *
378         * @throws ParseException If the JSON object couldn't be parsed to an
379         *                        octet sequence JWK.
380         */
381        public static OctetSequenceKey parse(final JSONObject jsonObject) 
382                throws ParseException {
383
384                // Parse the mandatory parameters first
385                Base64URL k = new Base64URL(JSONObjectUtils.getString(jsonObject, "k"));
386
387                // Check key type
388                KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
389
390                if (kty != KeyType.OCT) {
391
392                        throw new ParseException("The key type \"kty\" must be oct", 0);
393                }
394
395                // Get optional key use
396                KeyUse use = null;
397
398                if (jsonObject.containsKey("use")) {
399                        use = KeyUse.parse(JSONObjectUtils.getString(jsonObject, "use"));
400                }
401
402                // Get optional key operations
403                Set<KeyOperation> ops = null;
404
405                if (jsonObject.containsKey("key_ops")) {
406                        ops = KeyOperation.parse(JSONObjectUtils.getStringList(jsonObject, "key_ops"));
407                }
408
409                // Get optional intended algorithm
410                Algorithm alg = null;
411
412                if (jsonObject.containsKey("alg")) {
413                        alg = new Algorithm(JSONObjectUtils.getString(jsonObject, "alg"));
414                }
415
416                // Get optional key ID
417                String kid = null;
418
419                if (jsonObject.containsKey("kid")) {
420                        kid = JSONObjectUtils.getString(jsonObject, "kid");
421                }
422
423                // Get optional X.509 cert URL
424                URL x5u = null;
425
426                if (jsonObject.containsKey("x5u")) {
427                        x5u = JSONObjectUtils.getURL(jsonObject, "x5u");        
428                }
429
430                // Get optional X.509 cert thumbprint
431                Base64URL x5t = null;
432
433                if (jsonObject.containsKey("x5t")) {
434                        x5t = new Base64URL(JSONObjectUtils.getString(jsonObject, "x5t"));
435                }
436
437                // Get optional X.509 cert chain
438                List<Base64> x5c = null;
439
440                if (jsonObject.containsKey("x5c")) {
441                        x5c = X509CertChainUtils.parseX509CertChain(JSONObjectUtils.getJSONArray(jsonObject, "x5c"));   
442                }
443
444                return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5c);
445        }
446}