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$ (2015-01-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                 * Creates a new octet sequence JWK builder.
130                 *
131                 * @param key The key value. Must not be empty byte array or
132                 *            {@code null}.
133                 */
134                public Builder(final byte[] key) {
135
136                        if (key == null || key.length == 0) {
137                                throw new IllegalArgumentException("The key value must not be empty or null");
138                        }
139
140                        k = Base64URL.encode(key);
141                }
142
143
144                /**
145                 * Sets the use ({@code use}) of the JWK.
146                 *
147                 * @param use The key use, {@code null} if not specified or if
148                 *            the key is intended for signing as well as
149                 *            encryption.
150                 *
151                 * @return This builder.
152                 */
153                public Builder keyUse(final KeyUse use) {
154
155                        this.use = use;
156                        return this;
157                }
158
159
160                /**
161                 * Sets the operations ({@code key_ops}) of the JWK (for a
162                 * non-public key).
163                 *
164                 * @param ops The key operations, {@code null} if not
165                 *            specified.
166                 *
167                 * @return This builder.
168                 */
169                public Builder keyOperations(final Set<KeyOperation> ops) {
170
171                        this.ops = ops;
172                        return this;
173                }
174
175
176                /**
177                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
178                 *
179                 * @param alg The intended JOSE algorithm, {@code null} if not 
180                 *            specified.
181                 *
182                 * @return This builder.
183                 */
184                public Builder algorithm(final Algorithm alg) {
185
186                        this.alg = alg;
187                        return this;
188                }
189
190                /**
191                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 
192                 * to match a specific key. This can be used, for instance, to 
193                 * choose a key within a {@link JWKSet} during key rollover. 
194                 * The key ID may also correspond to a JWS/JWE {@code kid} 
195                 * header parameter value.
196                 *
197                 * @param kid The key ID, {@code null} if not specified.
198                 *
199                 * @return This builder.
200                 */
201                public Builder keyID(final String kid) {
202
203                        this.kid = kid;
204                        return this;
205                }
206
207
208                /**
209                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
210                 *
211                 * @param x5u The X.509 certificate URL, {@code null} if not 
212                 *            specified.
213                 *
214                 * @return This builder.
215                 */
216                public Builder x509CertURL(final URL x5u) {
217
218                        this.x5u = x5u;
219                        return this;
220                }
221
222
223                /**
224                 * Sets the X.509 certificate thumbprint ({@code x5t}) of the
225                 * JWK.
226                 *
227                 * @param x5t The X.509 certificate thumbprint, {@code null} if 
228                 *            not specified.
229                 *
230                 * @return This builder.
231                 */
232                public Builder x509CertThumbprint(final Base64URL x5t) {
233
234                        this.x5t = x5t;
235                        return this;
236                }
237
238                /**
239                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
240                 *
241                 * @param x5c The X.509 certificate chain as a unmodifiable 
242                 *            list, {@code null} if not specified.
243                 *
244                 * @return This builder.
245                 */
246                public Builder x509CertChain(final List<Base64> x5c) {
247
248                        this.x5c = x5c;
249                        return this;
250                }
251
252                /**
253                 * Builds a new octet sequence JWK.
254                 *
255                 * @return The octet sequence JWK.
256                 *
257                 * @throws IllegalStateException If the JWK parameters were
258                 *                               inconsistently specified.
259                 */
260                public OctetSequenceKey build() {
261
262                        try {
263                                return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5c);
264
265                        } catch (IllegalArgumentException e) {
266
267                                throw new IllegalStateException(e.getMessage(), e);
268                        }
269                }
270        }
271
272        
273        /**
274         * Creates a new octet sequence JSON Web Key (JWK) with the specified
275         * parameters.
276         *
277         * @param k   The key value. It is represented as the Base64URL 
278         *            encoding of value's big endian representation. Must not 
279         *            be {@code null}.
280         * @param use The key use, {@code null} if not specified or if the key
281         *            is intended for signing as well as encryption.
282         * @param ops The key operations, {@code null} if not specified.
283         * @param alg The intended JOSE algorithm for the key, {@code null} if
284         *            not specified.
285         * @param kid The key ID. {@code null} if not specified.
286         * @param x5u The X.509 certificate URL, {@code null} if not specified.
287         * @param x5t The X.509 certificate thumbprint, {@code null} if not
288         *            specified.
289         * @param x5c The X.509 certificate chain, {@code null} if not 
290         *            specified.
291         */
292        public OctetSequenceKey(final Base64URL k,
293                                final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
294                                final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
295        
296                super(KeyType.OCT, use, ops, alg, kid, x5u, x5t, x5c);
297
298                if (k == null) {
299                        throw new IllegalArgumentException("The key value must not be null");
300                }
301
302                this.k = k;
303        }
304    
305
306        /**
307         * Returns the value of this octet sequence key. 
308         *
309         * @return The key value. It is represented as the Base64URL encoding 
310         *         of the coordinate's big endian representation.
311         */
312        public Base64URL getKeyValue() {
313
314                return k;
315        }
316        
317        
318        /**
319         * Returns a copy of this octet sequence key value as a byte array.
320         * 
321         * @return The key value as a byte array.
322         */
323        public byte[] toByteArray() {
324
325                return getKeyValue().decode();
326        }
327
328
329        /**
330         * Octet sequence (symmetric) keys are never considered public, this 
331         * method always returns {@code true}.
332         *
333         * @return {@code true}
334         */
335        @Override
336        public boolean isPrivate() {
337
338                return true;
339        }
340
341
342        /**
343         * Octet sequence (symmetric) keys are never considered public, this 
344         * method always returns {@code null}.
345         *
346         * @return {@code null}
347         */
348        @Override
349        public OctetSequenceKey toPublicJWK() {
350
351                return null;
352        }
353        
354
355        @Override
356        public JSONObject toJSONObject() {
357
358                JSONObject o = super.toJSONObject();
359
360                // Append key value
361                o.put("k", k.toString());
362                
363                return o;
364        }
365
366
367        /**
368         * Parses an octet sequence JWK from the specified JSON object string 
369         * representation.
370         *
371         * @param s The JSON object string to parse. Must not be {@code null}.
372         *
373         * @return The octet sequence JWK.
374         *
375         * @throws ParseException If the string couldn't be parsed to an octet
376         *                        sequence JWK.
377         */
378        public static OctetSequenceKey parse(final String s)
379                throws ParseException {
380
381                return parse(JSONObjectUtils.parseJSONObject(s));
382        }
383
384        
385        /**
386         * Parses an octet sequence JWK from the specified JSON object 
387         * representation.
388         *
389         * @param jsonObject The JSON object to parse. Must not be 
390         *                   @code null}.
391         *
392         * @return The octet sequence JWK.
393         *
394         * @throws ParseException If the JSON object couldn't be parsed to an
395         *                        octet sequence JWK.
396         */
397        public static OctetSequenceKey parse(final JSONObject jsonObject) 
398                throws ParseException {
399
400                // Parse the mandatory parameters first
401                Base64URL k = new Base64URL(JSONObjectUtils.getString(jsonObject, "k"));
402
403                // Check key type
404                KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
405
406                if (kty != KeyType.OCT) {
407
408                        throw new ParseException("The key type \"kty\" must be oct", 0);
409                }
410
411                // Get optional key use
412                KeyUse use = null;
413
414                if (jsonObject.containsKey("use")) {
415                        use = KeyUse.parse(JSONObjectUtils.getString(jsonObject, "use"));
416                }
417
418                // Get optional key operations
419                Set<KeyOperation> ops = null;
420
421                if (jsonObject.containsKey("key_ops")) {
422                        ops = KeyOperation.parse(JSONObjectUtils.getStringList(jsonObject, "key_ops"));
423                }
424
425                // Get optional intended algorithm
426                Algorithm alg = null;
427
428                if (jsonObject.containsKey("alg")) {
429                        alg = new Algorithm(JSONObjectUtils.getString(jsonObject, "alg"));
430                }
431
432                // Get optional key ID
433                String kid = null;
434
435                if (jsonObject.containsKey("kid")) {
436                        kid = JSONObjectUtils.getString(jsonObject, "kid");
437                }
438
439                // Get optional X.509 cert URL
440                URL x5u = null;
441
442                if (jsonObject.containsKey("x5u")) {
443                        x5u = JSONObjectUtils.getURL(jsonObject, "x5u");        
444                }
445
446                // Get optional X.509 cert thumbprint
447                Base64URL x5t = null;
448
449                if (jsonObject.containsKey("x5t")) {
450                        x5t = new Base64URL(JSONObjectUtils.getString(jsonObject, "x5t"));
451                }
452
453                // Get optional X.509 cert chain
454                List<Base64> x5c = null;
455
456                if (jsonObject.containsKey("x5c")) {
457                        x5c = X509CertChainUtils.parseX509CertChain(JSONObjectUtils.getJSONArray(jsonObject, "x5c"));   
458                }
459
460                return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5c);
461        }
462}