001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2024, 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 com.nimbusds.jose.Algorithm;
022import com.nimbusds.jose.JOSEException;
023import com.nimbusds.jose.util.Base64;
024import com.nimbusds.jose.util.*;
025import net.jcip.annotations.Immutable;
026
027import javax.crypto.SecretKey;
028import javax.crypto.spec.SecretKeySpec;
029import java.net.URI;
030import java.security.*;
031import java.text.ParseException;
032import java.util.*;
033
034
035/**
036 * {@link KeyType#OCT Octet sequence} JSON Web Key (JWK), used to represent
037 * symmetric keys. This class is immutable.
038 *
039 * <p>Octet sequence JWKs should specify the algorithm intended to be used with
040 * the key, unless the application uses other means or convention to determine
041 * the algorithm used.
042 *
043 * <p>Example JSON object representation of an octet sequence JWK:
044 *
045 * <pre>
046 * {
047 *   "kty" : "oct",
048 *   "alg" : "A128KW",
049 *   "k"   : "GawgguFyGrWKav7AX4VKUg"
050 * }
051 * </pre>
052 *
053 * <p>Use the builder to create a new octet JWK:
054 *
055 * <pre>
056 * OctetSequenceKey key = new OctetSequenceKey.Builder(bytes)
057 *      .keyID("123")
058 *      .build();
059 * </pre>
060 * 
061 * @author Justin Richer
062 * @author Vladimir Dzhuvinov
063 * @version 2024-10-31
064 */
065@Immutable
066public final class OctetSequenceKey extends JWK implements SecretJWK {
067
068
069        private static final long serialVersionUID = 1L;
070
071
072        /**
073         * The key value.
074         */
075        private final Base64URL k;
076
077
078        /**
079         * Builder for constructing octet sequence JWKs.
080         *
081         * <p>Example usage:
082         *
083         * <pre>
084         * OctetSequenceKey key = new OctetSequenceKey.Builder(k)
085         *     .algorithm(JWSAlgorithm.HS512)
086         *     .keyID("123")
087         *     .build();
088         * </pre>
089         */
090        public static class Builder {
091
092
093                /**
094                 * The key value.
095                 */
096                private final Base64URL k;
097
098
099                /**
100                 * The public key use, optional.
101                 */
102                private KeyUse use;
103
104
105                /**
106                 * The key operations, optional.
107                 */
108                private Set<KeyOperation> ops;
109
110
111                /**
112                 * The intended JOSE algorithm for the key, optional.
113                 */
114                private Algorithm alg;
115
116
117                /**
118                 * The key ID, optional.
119                 */
120                private String kid;
121
122
123                /**
124                 * X.509 certificate URL, optional.
125                 */
126                private URI x5u;
127
128
129                /**
130                 * X.509 certificate SHA-1 thumbprint, optional.
131                 */
132                @Deprecated
133                private Base64URL x5t;
134                
135                
136                /**
137                 * X.509 certificate SHA-256 thumbprint, optional.
138                 */
139                private Base64URL x5t256;
140
141
142                /**
143                 * The X.509 certificate chain, optional.
144                 */
145                private List<Base64> x5c;
146                
147                
148                /**
149                 * The key expiration time, optional.
150                 */
151                private Date exp;
152                
153                
154                /**
155                 * The key not-before time, optional.
156                 */
157                private Date nbf;
158                
159                
160                /**
161                 * The key issued-at time, optional.
162                 */
163                private Date iat;
164
165
166                /**
167                 * The key revocation, optional.
168                 */
169                private KeyRevocation revocation;
170                
171                
172                /**
173                 * Reference to the underlying key store, {@code null} if none.
174                 */
175                private KeyStore ks;
176
177
178                /**
179                 * Creates a new octet sequence JWK builder.
180                 *
181                 * @param k The key value. It is represented as the Base64URL 
182                 *          encoding of value's big endian representation. Must
183                 *          not be {@code null}.
184                 */
185                public Builder(final Base64URL k) {
186
187                        this.k = Objects.requireNonNull(k);
188                }
189
190
191                /**
192                 * Creates a new octet sequence JWK builder.
193                 *
194                 * @param key The key value. Must not be empty byte array or
195                 *            {@code null}.
196                 */
197                public Builder(final byte[] key) {
198
199                        this(Base64URL.encode(key));
200
201                        if (key.length == 0) {
202                                throw new IllegalArgumentException("The key must have a positive length");
203                        }
204                }
205
206
207                /**
208                 * Creates a new octet sequence JWK builder.
209                 *
210                 * @param secretKey The secret key to represent. Must not be
211                 *                  {@code null}.
212                 */
213                public Builder(final SecretKey secretKey) {
214
215                        this(secretKey.getEncoded());
216                }
217                
218                
219                /**
220                 * Creates a new octet sequence JWK builder.
221                 *
222                 * @param octJWK The octet sequence JWK to start with. Must not
223                 *               be {@code null}.
224                 */
225                public Builder(final OctetSequenceKey octJWK) {
226                        
227                        k = octJWK.k;
228                        use = octJWK.getKeyUse();
229                        ops = octJWK.getKeyOperations();
230                        alg = octJWK.getAlgorithm();
231                        kid = octJWK.getKeyID();
232                        x5u = octJWK.getX509CertURL();
233                        x5t = octJWK.getX509CertThumbprint();
234                        x5t256 = octJWK.getX509CertSHA256Thumbprint();
235                        x5c = octJWK.getX509CertChain();
236                        exp = octJWK.getExpirationTime();
237                        nbf = octJWK.getNotBeforeTime();
238                        iat = octJWK.getIssueTime();
239                        revocation = octJWK.getKeyRevocation();
240                        ks = octJWK.getKeyStore();
241                }
242
243
244                /**
245                 * Sets the use ({@code use}) of the JWK.
246                 *
247                 * @param use The key use, {@code null} if not specified or if
248                 *            the key is intended for signing as well as
249                 *            encryption.
250                 *
251                 * @return This builder.
252                 */
253                public Builder keyUse(final KeyUse use) {
254
255                        this.use = use;
256                        return this;
257                }
258
259
260                /**
261                 * Sets the operations ({@code key_ops}) of the JWK (for a
262                 * non-public key).
263                 *
264                 * @param ops The key operations, {@code null} if not
265                 *            specified.
266                 *
267                 * @return This builder.
268                 */
269                public Builder keyOperations(final Set<KeyOperation> ops) {
270
271                        this.ops = ops;
272                        return this;
273                }
274
275
276                /**
277                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
278                 *
279                 * @param alg The intended JOSE algorithm, {@code null} if not 
280                 *            specified.
281                 *
282                 * @return This builder.
283                 */
284                public Builder algorithm(final Algorithm alg) {
285
286                        this.alg = alg;
287                        return this;
288                }
289
290                /**
291                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 
292                 * to match a specific key. This can be used, for instance, to 
293                 * choose a key within a {@link JWKSet} during key rollover. 
294                 * The key ID may also correspond to a JWS/JWE {@code kid} 
295                 * header parameter value.
296                 *
297                 * @param kid The key ID, {@code null} if not specified.
298                 *
299                 * @return This builder.
300                 */
301                public Builder keyID(final String kid) {
302
303                        this.kid = kid;
304                        return this;
305                }
306
307
308                /**
309                 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK
310                 * thumbprint (RFC 7638). The key ID can be used to match a
311                 * specific key. This can be used, for instance, to choose a
312                 * key within a {@link JWKSet} during key rollover. The key ID
313                 * may also correspond to a JWS/JWE {@code kid} header
314                 * parameter value.
315                 *
316                 * @return This builder.
317                 *
318                 * @throws JOSEException If the SHA-256 hash algorithm is not
319                 *                       supported.
320                 */
321                public Builder keyIDFromThumbprint()
322                        throws JOSEException {
323
324                        return keyIDFromThumbprint("SHA-256");
325                }
326
327
328                /**
329                 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint
330                 * (RFC 7638). The key ID can be used to match a specific key.
331                 * This can be used, for instance, to choose a key within a
332                 * {@link JWKSet} during key rollover. The key ID may also
333                 * correspond to a JWS/JWE {@code kid} header parameter value.
334                 *
335                 * @param hashAlg The hash algorithm for the JWK thumbprint
336                 *                computation. Must not be {@code null}.
337                 *
338                 * @return This builder.
339                 *
340                 * @throws JOSEException If the hash algorithm is not
341                 *                       supported.
342                 */
343                public Builder keyIDFromThumbprint(final String hashAlg)
344                        throws JOSEException {
345
346                        // Put mandatory params in sorted order
347                        LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
348                        requiredParams.put(JWKParameterNames.OCT_KEY_VALUE, k.toString());
349                        requiredParams.put(JWKParameterNames.KEY_TYPE, KeyType.OCT.getValue());
350                        this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString();
351                        return this;
352                }
353
354
355                /**
356                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
357                 *
358                 * @param x5u The X.509 certificate URL, {@code null} if not 
359                 *            specified.
360                 *
361                 * @return This builder.
362                 */
363                public Builder x509CertURL(final URI x5u) {
364
365                        this.x5u = x5u;
366                        return this;
367                }
368                
369                
370                /**
371                 * Sets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of
372                 * the JWK.
373                 *
374                 * @param x5t The X.509 certificate SHA-1 thumbprint,
375                 *            {@code null} if not specified.
376                 *
377                 * @return This builder.
378                 */
379                @Deprecated
380                public Builder x509CertThumbprint(final Base64URL x5t) {
381                        
382                        this.x5t = x5t;
383                        return this;
384                }
385                
386                
387                /**
388                 * Sets the X.509 certificate SHA-256 thumbprint
389                 * ({@code x5t#S256}) of the JWK.
390                 *
391                 * @param x5t256 The X.509 certificate SHA-256 thumbprint,
392                 *               {@code null} if not specified.
393                 *
394                 * @return This builder.
395                 */
396                public Builder x509CertSHA256Thumbprint(final Base64URL x5t256) {
397                        
398                        this.x5t256 = x5t256;
399                        return this;
400                }
401                
402
403                /**
404                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
405                 *
406                 * @param x5c The X.509 certificate chain as a unmodifiable 
407                 *            list, {@code null} if not specified.
408                 *
409                 * @return This builder.
410                 */
411                public Builder x509CertChain(final List<Base64> x5c) {
412
413                        this.x5c = x5c;
414                        return this;
415                }
416                
417                
418                /**
419                 * Sets the expiration time ({@code exp}) of the JWK.
420                 *
421                 * @param exp The expiration time, {@code null} if not
422                 *            specified.
423                 *
424                 * @return This builder.
425                 */
426                public Builder expirationTime(final Date exp) {
427                        
428                        this.exp = exp;
429                        return this;
430                }
431                
432                
433                /**
434                 * Sets the not-before time ({@code nbf}) of the JWK.
435                 *
436                 * @param nbf The not-before time, {@code null} if not
437                 *            specified.
438                 *
439                 * @return This builder.
440                 */
441                public Builder notBeforeTime(final Date nbf) {
442                        
443                        this.nbf = nbf;
444                        return this;
445                }
446                
447                
448                /**
449                 * Sets the issued-at time ({@code iat}) of the JWK.
450                 *
451                 * @param iat The issued-at time, {@code null} if not
452                 *            specified.
453                 *
454                 * @return This builder.
455                 */
456                public Builder issueTime(final Date iat) {
457                        
458                        this.iat = iat;
459                        return this;
460                }
461
462
463                /**
464                 * Sets the revocation ({@code revoked}) of the JWK.
465                 *
466                 * @param revocation The key revocation, {@code null} if not
467                 *                   specified.
468                 *
469                 * @return This builder.
470                 */
471                public Builder keyRevocation(final KeyRevocation revocation) {
472
473                        this.revocation = revocation;
474                        return this;
475                }
476                
477                
478                /**
479                 * Sets the underlying key store.
480                 *
481                 * @param keyStore Reference to the underlying key store,
482                 *                 {@code null} if none.
483                 *
484                 * @return This builder.
485                 */
486                public Builder keyStore(final KeyStore keyStore) {
487                        
488                        this.ks = keyStore;
489                        return this;
490                }
491                
492
493                /**
494                 * Builds a new octet sequence JWK.
495                 *
496                 * @return The octet sequence JWK.
497                 *
498                 * @throws IllegalStateException If the JWK parameters were
499                 *                               inconsistently specified.
500                 */
501                public OctetSequenceKey build() {
502
503                        try {
504                                return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, revocation, ks);
505
506                        } catch (IllegalArgumentException e) {
507
508                                throw new IllegalStateException(e.getMessage(), e);
509                        }
510                }
511        }
512
513        
514        /**
515         * Creates a new octet sequence JSON Web Key (JWK) with the specified
516         * parameters.
517         *
518         * @param k      The key value. It is represented as the Base64URL
519         *               encoding of the value's big endian representation.
520         *               Must not be {@code null}.
521         * @param use    The key use, {@code null} if not specified or if the
522         *               key is intended for signing as well as encryption.
523         * @param ops    The key operations, {@code null} if not specified.
524         * @param alg    The intended JOSE algorithm for the key, {@code null}
525         *               if not specified.
526         * @param kid    The key ID. {@code null} if not specified.
527         * @param x5u    The X.509 certificate URL, {@code null} if not specified.
528         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
529         *               if not specified.
530         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
531         *               if not specified.
532         * @param x5c    The X.509 certificate chain, {@code null} if not
533         *               specified.
534         * @param ks     Reference to the underlying key store, {@code null} if
535         *               not specified.
536         */
537        @Deprecated
538        public OctetSequenceKey(final Base64URL k,
539                                final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
540                                final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
541                                final KeyStore ks) {
542        
543                this(k, use, ops, alg, kid, x5u, x5t, x5t256, x5c, null, null, null, ks);
544        }
545
546        
547        /**
548         * Creates a new octet sequence JSON Web Key (JWK) with the specified
549         * parameters.
550         *
551         * @param k      The key value. It is represented as the Base64URL
552         *               encoding of the value's big endian representation.
553         *               Must not be {@code null}.
554         * @param use    The key use, {@code null} if not specified or if the
555         *               key is intended for signing as well as encryption.
556         * @param ops    The key operations, {@code null} if not specified.
557         * @param alg    The intended JOSE algorithm for the key, {@code null}
558         *               if not specified.
559         * @param kid    The key ID. {@code null} if not specified.
560         * @param x5u    The X.509 certificate URL, {@code null} if not specified.
561         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
562         *               if not specified.
563         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
564         *               if not specified.
565         * @param x5c    The X.509 certificate chain, {@code null} if not
566         *               specified.
567         * @param exp    The key expiration time, {@code null} if not
568         *               specified.
569         * @param nbf    The key not-before time, {@code null} if not
570         *               specified.
571         * @param iat    The key issued-at time, {@code null} if not specified.
572         * @param ks     Reference to the underlying key store, {@code null} if
573         *               not specified.
574         */
575        @Deprecated
576        public OctetSequenceKey(final Base64URL k,
577                                final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
578                                final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
579                                final Date exp, final Date nbf, final Date iat,
580                                final KeyStore ks) {
581        
582                this(k, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, null, ks);
583        }
584
585
586        /**
587         * Creates a new octet sequence JSON Web Key (JWK) with the specified
588         * parameters.
589         *
590         * @param k          The key value. It is represented as the Base64URL
591         *                   encoding of the value's big endian representation.
592         *                   Must not be {@code null}.
593         * @param use        The key use, {@code null} if not specified or if
594         *                   the key is intended for signing as well as
595         *                   encryption.
596         * @param ops        The key operations, {@code null} if not specified.
597         * @param alg        The intended JOSE algorithm for the key,
598         *                   {@code null} if not specified.
599         * @param kid        The key ID. {@code null} if not specified.
600         * @param x5u        The X.509 certificate URL, {@code null} if not
601         *                   specified.
602         * @param x5t        The X.509 certificate SHA-1 thumbprint,
603         *                   {@code null} if not specified.
604         * @param x5t256     The X.509 certificate SHA-256 thumbprint,
605         *                   {@code null} if not specified.
606         * @param x5c        The X.509 certificate chain, {@code null} if not
607         *                   specified.
608         * @param exp        The key expiration time, {@code null} if not
609         *                   specified.
610         * @param nbf        The key not-before time, {@code null} if not
611         *                   specified.
612         * @param iat        The key issued-at time, {@code null} if not
613         *                   specified.
614         * @param revocation The key revocation, {@code null} if not specified.
615         * @param ks         Reference to the underlying key store,
616         *                   {@code null} if not specified.
617         */
618        public OctetSequenceKey(final Base64URL k,
619                                final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
620                                final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
621                                final Date exp, final Date nbf, final Date iat, final KeyRevocation revocation,
622                                final KeyStore ks) {
623
624                super(KeyType.OCT, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, revocation, ks);
625                this.k = Objects.requireNonNull(k, "The key value must not be null");
626        }
627    
628
629        /**
630         * Returns the value of this octet sequence key. 
631         *
632         * @return The key value. It is represented as the Base64URL encoding
633         *         of the value's big endian representation.
634         */
635        public Base64URL getKeyValue() {
636
637                return k;
638        }
639        
640        
641        /**
642         * Returns a copy of this octet sequence key value as a byte array.
643         * 
644         * @return The key value as a byte array.
645         */
646        public byte[] toByteArray() {
647
648                return getKeyValue().decode();
649        }
650
651
652        /**
653         * Returns a secret key representation of this octet sequence key.
654         *
655         * @return The secret key representation, with an algorithm set to
656         *         {@code NONE}.
657         */
658        @Override
659        public SecretKey toSecretKey() {
660
661                return toSecretKey("NONE");
662        }
663
664
665        /**
666         * Returns a secret key representation of this octet sequence key with
667         * the specified Java Cryptography Architecture (JCA) algorithm.
668         *
669         * @param jcaAlg The JCA algorithm. Must not be {@code null}.
670         *
671         * @return The secret key representation.
672         */
673        public SecretKey toSecretKey(final String jcaAlg) {
674
675                return new SecretKeySpec(toByteArray(), jcaAlg);
676        }
677
678
679        @Override
680        public LinkedHashMap<String,?> getRequiredParams() {
681
682                // Put mandatory params in sorted order
683                LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
684                requiredParams.put(JWKParameterNames.OCT_KEY_VALUE, k.toString());
685                requiredParams.put(JWKParameterNames.KEY_TYPE, getKeyType().toString());
686                return requiredParams;
687        }
688
689
690        /**
691         * Octet sequence (symmetric) keys are never considered public, this 
692         * method always returns {@code true}.
693         *
694         * @return {@code true}
695         */
696        @Override
697        public boolean isPrivate() {
698
699                return true;
700        }
701
702
703        /**
704         * Octet sequence (symmetric) keys are never considered public, this 
705         * method always returns {@code null}.
706         *
707         * @return {@code null}
708         */
709        @Override
710        public OctetSequenceKey toPublicJWK() {
711
712                return null;
713        }
714
715
716        @Override
717        public OctetSequenceKey toRevokedJWK(final KeyRevocation keyRevocation) {
718
719                if (getKeyRevocation() != null) {
720                        throw new IllegalStateException("Already revoked");
721                }
722
723                return new OctetSequenceKey.Builder(this)
724                        .keyRevocation(Objects.requireNonNull(keyRevocation))
725                        .build();
726        }
727
728
729        @Override
730        public int size() {
731
732                try {
733                        return ByteUtils.safeBitLength(k.decode());
734                } catch (IntegerOverflowException e) {
735                        throw new ArithmeticException(e.getMessage());
736                }
737        }
738
739
740        @Override
741        public Map<String, Object> toJSONObject() {
742
743                Map<String, Object> o = super.toJSONObject();
744
745                // Append key value
746                o.put(JWKParameterNames.OCT_KEY_VALUE, k.toString());
747                
748                return o;
749        }
750
751
752        /**
753         * Parses an octet sequence JWK from the specified JSON object string 
754         * representation.
755         *
756         * @param s The JSON object string to parse. Must not be {@code null}.
757         *
758         * @return The octet sequence JWK.
759         *
760         * @throws ParseException If the string couldn't be parsed to an octet
761         *                        sequence JWK.
762         */
763        public static OctetSequenceKey parse(final String s)
764                throws ParseException {
765
766                return parse(JSONObjectUtils.parse(s));
767        }
768
769        
770        /**
771         * Parses an octet sequence JWK from the specified JSON object 
772         * representation.
773         *
774         * @param jsonObject The JSON object to parse. Must not be 
775         *                   {@code null}.
776         *
777         * @return The octet sequence JWK.
778         *
779         * @throws ParseException If the JSON object couldn't be parsed to an
780         *                        octet sequence JWK.
781         */
782        public static OctetSequenceKey parse(final Map<String, Object> jsonObject) 
783                throws ParseException {
784                
785                // Check the key type
786                if (! KeyType.OCT.equals(JWKMetadata.parseKeyType(jsonObject))) {
787                        throw new ParseException("The key type " + JWKParameterNames.KEY_TYPE + " must be " + KeyType.OCT.getValue(), 0);
788                }
789
790                // Parse the mandatory parameter
791                Base64URL k = JSONObjectUtils.getBase64URL(jsonObject, JWKParameterNames.OCT_KEY_VALUE);
792
793                try {
794                        return new OctetSequenceKey(k,
795                                JWKMetadata.parseKeyUse(jsonObject),
796                                JWKMetadata.parseKeyOperations(jsonObject),
797                                JWKMetadata.parseAlgorithm(jsonObject),
798                                JWKMetadata.parseKeyID(jsonObject),
799                                JWKMetadata.parseX509CertURL(jsonObject),
800                                JWKMetadata.parseX509CertThumbprint(jsonObject),
801                                JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject),
802                                JWKMetadata.parseX509CertChain(jsonObject),
803                                JWKMetadata.parseExpirationTime(jsonObject),
804                                JWKMetadata.parseNotBeforeTime(jsonObject),
805                                JWKMetadata.parseIssueTime(jsonObject),
806                                JWKMetadata.parseKeyRevocation(jsonObject),
807                                null // key store
808                        );
809                } catch (Exception e) {
810                        throw new ParseException(e.getMessage(), 0);
811                }
812        }
813        
814        
815        /**
816         * Loads an octet sequence JWK from the specified JCA key store.
817         *
818         * @param keyStore The key store. Must not be {@code null}.
819         * @param alias    The alias. Must not be {@code null}.
820         * @param pin      The pin to unlock the private key if any, empty or
821         *                 {@code null} if not required.
822         *
823         * @return The octet sequence JWK, {@code null} if no key with the
824         *         specified alias was found.
825         *
826         * @throws KeyStoreException On a key store exception.
827         * @throws JOSEException     If octet sequence key loading failed.
828         */
829        public static OctetSequenceKey load(final KeyStore keyStore, final String alias, final char[] pin)
830                throws KeyStoreException, JOSEException {
831                
832                Key key;
833                try {
834                        key = keyStore.getKey(alias, pin);
835                } catch (UnrecoverableKeyException | NoSuchAlgorithmException e) {
836                        throw new JOSEException("Couldn't retrieve secret key (bad pin?): " + e.getMessage(), e);
837                }
838                
839                if (! (key instanceof SecretKey)) {
840                        return null;
841                }
842                
843                return new OctetSequenceKey.Builder((SecretKey)key)
844                        .keyID(alias)
845                        .keyStore(keyStore)
846                        .build();
847        }
848
849        
850        @Override
851        public boolean equals(Object o) {
852                if (this == o) return true;
853                if (!(o instanceof OctetSequenceKey)) return false;
854                if (!super.equals(o)) return false;
855                OctetSequenceKey that = (OctetSequenceKey) o;
856                return Objects.equals(k, that.k);
857        }
858
859        
860        @Override
861        public int hashCode() {
862                return Objects.hash(super.hashCode(), k);
863        }
864}