001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.jwk;
019
020
021import java.net.URI;
022import java.security.*;
023import java.text.ParseException;
024import java.util.LinkedHashMap;
025import java.util.List;
026import java.util.Set;
027import javax.crypto.SecretKey;
028import javax.crypto.spec.SecretKeySpec;
029
030import com.nimbusds.jose.Algorithm;
031import com.nimbusds.jose.JOSEException;
032import com.nimbusds.jose.util.*;
033import net.jcip.annotations.Immutable;
034import net.minidev.json.JSONObject;
035
036
037/**
038 * {@link KeyType#OCT Octet sequence} JSON Web Key (JWK), used to represent
039 * symmetric keys. This class is immutable.
040 *
041 * <p>Octet sequence JWKs should specify the algorithm intended to be used with
042 * the key, unless the application uses other means or convention to determine
043 * the algorithm used.
044 *
045 * <p>Example JSON object representation of an octet sequence JWK:
046 *
047 * <pre>
048 * {
049 *   "kty" : "oct",
050 *   "alg" : "A128KW",
051 *   "k"   : "GawgguFyGrWKav7AX4VKUg"
052 * }
053 * </pre>
054 *
055 * <p>Use the builder to create a new octet JWK:
056 *
057 * <pre>
058 * OctetSequenceKey key = new OctetSequenceKey.Builder(bytes)
059 *      .keyID("123")
060 *      .build();
061 * </pre>
062 * 
063 * @author Justin Richer
064 * @author Vladimir Dzhuvinov
065 * @version 2017-06-01
066 */
067@Immutable
068public final class OctetSequenceKey extends JWK implements SecretJWK {
069
070
071        private static final long serialVersionUID = 1L;
072
073
074        /**
075         * The key value.
076         */
077        private final Base64URL k;
078
079
080        /**
081         * Builder for constructing octet sequence JWKs.
082         *
083         * <p>Example usage:
084         *
085         * <pre>
086         * OctetSequenceKey key = new OctetSequenceKey.Builder(k)
087         *     .algorithm(JWSAlgorithm.HS512)
088         *     .keyID("123")
089         *     .build();
090         * </pre>
091         */
092        public static class Builder {
093
094
095                /**
096                 * The key value.
097                 */
098                private final Base64URL k;
099
100
101                /**
102                 * The public key use, optional.
103                 */
104                private KeyUse use;
105
106
107                /**
108                 * The key operations, optional.
109                 */
110                private Set<KeyOperation> ops;
111
112
113                /**
114                 * The intended JOSE algorithm for the key, optional.
115                 */
116                private Algorithm alg;
117
118
119                /**
120                 * The key ID, optional.
121                 */
122                private String kid;
123
124
125                /**
126                 * X.509 certificate URL, optional.
127                 */
128                private URI x5u;
129
130
131                /**
132                 * X.509 certificate SHA-1 thumbprint, optional.
133                 */
134                @Deprecated
135                private Base64URL x5t;
136                
137                
138                /**
139                 * X.509 certificate SHA-256 thumbprint, optional.
140                 */
141                private Base64URL x5t256;
142
143
144                /**
145                 * The X.509 certificate chain, optional.
146                 */
147                private List<Base64> x5c;
148                
149                
150                /**
151                 * Reference to the underlying key store, {@code null} if none.
152                 */
153                private KeyStore ks;
154
155
156                /**
157                 * Creates a new octet sequence JWK builder.
158                 *
159                 * @param k The key value. It is represented as the Base64URL 
160                 *          encoding of value's big endian representation. Must
161                 *          not be {@code null}.
162                 */
163                public Builder(final Base64URL k) {
164
165                        if (k == null) {
166                                throw new IllegalArgumentException("The key value must not be null");
167                        }
168
169                        this.k = k;
170                }
171
172
173                /**
174                 * Creates a new octet sequence JWK builder.
175                 *
176                 * @param key The key value. Must not be empty byte array or
177                 *            {@code null}.
178                 */
179                public Builder(final byte[] key) {
180
181                        this(Base64URL.encode(key));
182
183                        if (key.length == 0) {
184                                throw new IllegalArgumentException("The key must have a positive length");
185                        }
186                }
187
188
189                /**
190                 * Creates a new octet sequence JWK builder.
191                 *
192                 * @param secretKey The secret key to represent. Must not be
193                 *                  {@code null}.
194                 */
195                public Builder(final SecretKey secretKey) {
196
197                        this(secretKey.getEncoded());
198                }
199
200
201                /**
202                 * Sets the use ({@code use}) of the JWK.
203                 *
204                 * @param use The key use, {@code null} if not specified or if
205                 *            the key is intended for signing as well as
206                 *            encryption.
207                 *
208                 * @return This builder.
209                 */
210                public Builder keyUse(final KeyUse use) {
211
212                        this.use = use;
213                        return this;
214                }
215
216
217                /**
218                 * Sets the operations ({@code key_ops}) of the JWK (for a
219                 * non-public key).
220                 *
221                 * @param ops The key operations, {@code null} if not
222                 *            specified.
223                 *
224                 * @return This builder.
225                 */
226                public Builder keyOperations(final Set<KeyOperation> ops) {
227
228                        this.ops = ops;
229                        return this;
230                }
231
232
233                /**
234                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
235                 *
236                 * @param alg The intended JOSE algorithm, {@code null} if not 
237                 *            specified.
238                 *
239                 * @return This builder.
240                 */
241                public Builder algorithm(final Algorithm alg) {
242
243                        this.alg = alg;
244                        return this;
245                }
246
247                /**
248                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 
249                 * to match a specific key. This can be used, for instance, to 
250                 * choose a key within a {@link JWKSet} during key rollover. 
251                 * The key ID may also correspond to a JWS/JWE {@code kid} 
252                 * header parameter value.
253                 *
254                 * @param kid The key ID, {@code null} if not specified.
255                 *
256                 * @return This builder.
257                 */
258                public Builder keyID(final String kid) {
259
260                        this.kid = kid;
261                        return this;
262                }
263
264
265                /**
266                 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK
267                 * thumbprint (RFC 7638). The key ID can be used to match a
268                 * specific key. This can be used, for instance, to choose a
269                 * key within a {@link JWKSet} during key rollover. The key ID
270                 * may also correspond to a JWS/JWE {@code kid} header
271                 * parameter value.
272                 *
273                 * @return This builder.
274                 *
275                 * @throws JOSEException If the SHA-256 hash algorithm is not
276                 *                       supported.
277                 */
278                public Builder keyIDFromThumbprint()
279                        throws JOSEException {
280
281                        return keyIDFromThumbprint("SHA-256");
282                }
283
284
285                /**
286                 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint
287                 * (RFC 7638). The key ID can be used to match a specific key.
288                 * This can be used, for instance, to choose a key within a
289                 * {@link JWKSet} during key rollover. The key ID may also
290                 * correspond to a JWS/JWE {@code kid} header parameter value.
291                 *
292                 * @param hashAlg The hash algorithm for the JWK thumbprint
293                 *                computation. Must not be {@code null}.
294                 *
295                 * @return This builder.
296                 *
297                 * @throws JOSEException If the hash algorithm is not
298                 *                       supported.
299                 */
300                public Builder keyIDFromThumbprint(final String hashAlg)
301                        throws JOSEException {
302
303                        // Put mandatory params in sorted order
304                        LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
305                        requiredParams.put("k", k.toString());
306                        requiredParams.put("kty", KeyType.OCT.getValue());
307                        this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString();
308                        return this;
309                }
310
311
312                /**
313                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
314                 *
315                 * @param x5u The X.509 certificate URL, {@code null} if not 
316                 *            specified.
317                 *
318                 * @return This builder.
319                 */
320                public Builder x509CertURL(final URI x5u) {
321
322                        this.x5u = x5u;
323                        return this;
324                }
325                
326                
327                /**
328                 * Sets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of
329                 * the JWK.
330                 *
331                 * @param x5t The X.509 certificate SHA-1 thumbprint,
332                 *            {@code null} if not specified.
333                 *
334                 * @return This builder.
335                 */
336                @Deprecated
337                public Builder x509CertThumbprint(final Base64URL x5t) {
338                        
339                        this.x5t = x5t;
340                        return this;
341                }
342                
343                
344                /**
345                 * Sets the X.509 certificate SHA-256 thumbprint
346                 * ({@code x5t#S256}) of the JWK.
347                 *
348                 * @param x5t256 The X.509 certificate SHA-256 thumbprint,
349                 *               {@code null} if not specified.
350                 *
351                 * @return This builder.
352                 */
353                public Builder x509CertSHA256Thumbprint(final Base64URL x5t256) {
354                        
355                        this.x5t256 = x5t256;
356                        return this;
357                }
358                
359
360                /**
361                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
362                 *
363                 * @param x5c The X.509 certificate chain as a unmodifiable 
364                 *            list, {@code null} if not specified.
365                 *
366                 * @return This builder.
367                 */
368                public Builder x509CertChain(final List<Base64> x5c) {
369
370                        this.x5c = x5c;
371                        return this;
372                }
373                
374                
375                /**
376                 * Sets the underlying key store.
377                 *
378                 * @param keyStore Reference to the underlying key store,
379                 *                 {@code null} if none.
380                 *
381                 * @return This builder.
382                 */
383                public Builder keyStore(final KeyStore keyStore) {
384                        
385                        this.ks = keyStore;
386                        return this;
387                }
388                
389
390                /**
391                 * Builds a new octet sequence JWK.
392                 *
393                 * @return The octet sequence JWK.
394                 *
395                 * @throws IllegalStateException If the JWK parameters were
396                 *                               inconsistently specified.
397                 */
398                public OctetSequenceKey build() {
399
400                        try {
401                                return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
402
403                        } catch (IllegalArgumentException e) {
404
405                                throw new IllegalStateException(e.getMessage(), e);
406                        }
407                }
408        }
409
410        
411        /**
412         * Creates a new octet sequence JSON Web Key (JWK) with the specified
413         * parameters.
414         *
415         * @param k      The key value. It is represented as the Base64URL
416         *               encoding of the value's big endian representation.
417         *               Must not be {@code null}.
418         * @param use    The key use, {@code null} if not specified or if the
419         *               key is intended for signing as well as encryption.
420         * @param ops    The key operations, {@code null} if not specified.
421         * @param alg    The intended JOSE algorithm for the key, {@code null}
422         *               if not specified.
423         * @param kid    The key ID. {@code null} if not specified.
424         * @param x5u    The X.509 certificate URL, {@code null} if not specified.
425         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
426         *               if not specified.
427         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
428         *               if not specified.
429         * @param x5c    The X.509 certificate chain, {@code null} if not
430         *               specified.
431         * @param ks     Reference to the underlying key store, {@code null} if
432         *               not specified.
433         */
434        public OctetSequenceKey(final Base64URL k,
435                                final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
436                                final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
437                                final KeyStore ks) {
438        
439                super(KeyType.OCT, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
440
441                if (k == null) {
442                        throw new IllegalArgumentException("The key value must not be null");
443                }
444
445                this.k = k;
446        }
447    
448
449        /**
450         * Returns the value of this octet sequence key. 
451         *
452         * @return The key value. It is represented as the Base64URL encoding
453         *         of the value's big endian representation.
454         */
455        public Base64URL getKeyValue() {
456
457                return k;
458        }
459        
460        
461        /**
462         * Returns a copy of this octet sequence key value as a byte array.
463         * 
464         * @return The key value as a byte array.
465         */
466        public byte[] toByteArray() {
467
468                return getKeyValue().decode();
469        }
470
471
472        /**
473         * Returns a secret key representation of this octet sequence key.
474         *
475         * @return The secret key representation, with an algorithm set to
476         *         {@code NONE}.
477         */
478        @Override
479        public SecretKey toSecretKey() {
480
481                return toSecretKey("NONE");
482        }
483
484
485        /**
486         * Returns a secret key representation of this octet sequence key with
487         * the specified Java Cryptography Architecture (JCA) algorithm.
488         *
489         * @param jcaAlg The JCA algorithm. Must not be {@code null}.
490         *
491         * @return The secret key representation.
492         */
493        public SecretKey toSecretKey(final String jcaAlg) {
494
495                return new SecretKeySpec(toByteArray(), jcaAlg);
496        }
497
498
499        @Override
500        public LinkedHashMap<String,?> getRequiredParams() {
501
502                // Put mandatory params in sorted order
503                LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
504                requiredParams.put("k", k.toString());
505                requiredParams.put("kty", getKeyType().toString());
506                return requiredParams;
507        }
508
509
510        /**
511         * Octet sequence (symmetric) keys are never considered public, this 
512         * method always returns {@code true}.
513         *
514         * @return {@code true}
515         */
516        @Override
517        public boolean isPrivate() {
518
519                return true;
520        }
521
522
523        /**
524         * Octet sequence (symmetric) keys are never considered public, this 
525         * method always returns {@code null}.
526         *
527         * @return {@code null}
528         */
529        @Override
530        public OctetSequenceKey toPublicJWK() {
531
532                return null;
533        }
534
535
536        @Override
537        public int size() {
538
539                try {
540                        return ByteUtils.safeBitLength(k.decode());
541                } catch (IntegerOverflowException e) {
542                        throw new ArithmeticException(e.getMessage());
543                }
544        }
545
546
547        @Override
548        public JSONObject toJSONObject() {
549
550                JSONObject o = super.toJSONObject();
551
552                // Append key value
553                o.put("k", k.toString());
554                
555                return o;
556        }
557
558
559        /**
560         * Parses an octet sequence JWK from the specified JSON object string 
561         * representation.
562         *
563         * @param s The JSON object string to parse. Must not be {@code null}.
564         *
565         * @return The octet sequence JWK.
566         *
567         * @throws ParseException If the string couldn't be parsed to an octet
568         *                        sequence JWK.
569         */
570        public static OctetSequenceKey parse(final String s)
571                throws ParseException {
572
573                return parse(JSONObjectUtils.parse(s));
574        }
575
576        
577        /**
578         * Parses an octet sequence JWK from the specified JSON object 
579         * representation.
580         *
581         * @param jsonObject The JSON object to parse. Must not be 
582         *                   {@code null}.
583         *
584         * @return The octet sequence JWK.
585         *
586         * @throws ParseException If the JSON object couldn't be parsed to an
587         *                        octet sequence JWK.
588         */
589        public static OctetSequenceKey parse(final JSONObject jsonObject) 
590                throws ParseException {
591
592                // Parse the mandatory parameters first
593                Base64URL k = new Base64URL(JSONObjectUtils.getString(jsonObject, "k"));
594
595                // Check key type
596                KeyType kty = JWKMetadata.parseKeyType(jsonObject);
597
598                if (kty != KeyType.OCT) {
599
600                        throw new ParseException("The key type \"kty\" must be oct", 0);
601                }
602
603                return new OctetSequenceKey(k,
604                        JWKMetadata.parseKeyUse(jsonObject),
605                        JWKMetadata.parseKeyOperations(jsonObject),
606                        JWKMetadata.parseAlgorithm(jsonObject),
607                        JWKMetadata.parseKeyID(jsonObject),
608                        JWKMetadata.parseX509CertURL(jsonObject),
609                        JWKMetadata.parseX509CertThumbprint(jsonObject),
610                        JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject),
611                        JWKMetadata.parseX509CertChain(jsonObject),
612                        null // key store
613                );
614        }
615        
616        
617        /**
618         * Loads an octet sequence JWK from the specified JCA key store.
619         *
620         * @param keyStore The key store. Must not be {@code null}.
621         * @param alias    The alias. Must not be {@code null}.
622         * @param pin      The pin to unlock the private key if any, empty or
623         *                 {@code null} if not required.
624         *
625         * @return The octet sequence JWK, {@code null} if no key with the
626         *         specified alias was found.
627         *
628         * @throws KeyStoreException On a key store exception.
629         * @throws JOSEException     If octet sequence key loading failed.
630         */
631        public static OctetSequenceKey load(final KeyStore keyStore, final String alias, final char[] pin)
632                throws KeyStoreException, JOSEException {
633                
634                Key key;
635                try {
636                        key = keyStore.getKey(alias, pin);
637                } catch (UnrecoverableKeyException | NoSuchAlgorithmException e) {
638                        throw new JOSEException("Couldn't retrieve secret key (bad pin?): " + e.getMessage(), e);
639                }
640                
641                if (! (key instanceof SecretKey)) {
642                        return null;
643                }
644                
645                return new OctetSequenceKey.Builder((SecretKey)key)
646                        .keyID(alias)
647                        .keyStore(keyStore)
648                        .build();
649        }
650}