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