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