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