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