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