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