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 2022-12-26
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                 * The key expiration time, optional.
190                 */
191                private Date exp;
192                
193                
194                /**
195                 * The key not-before time, optional.
196                 */
197                private Date nbf;
198                
199                
200                /**
201                 * The key issued-at time, optional.
202                 */
203                private Date iat;
204                
205                
206                /**
207                 * Reference to the underlying key store, {@code null} if none.
208                 */
209                private KeyStore ks;
210                
211                
212                /**
213                 * Creates a new Octet Key Pair JWK builder.
214                 *
215                 * @param crv The cryptographic curve. Must not be
216                 *            {@code null}.
217                 * @param x   The public 'x' parameter. Must not be 
218                 *            {@code null}.
219                 */
220                public Builder(final Curve crv, final Base64URL x) {
221                        
222                        if (crv == null) {
223                                throw new IllegalArgumentException("The curve must not be null");
224                        }
225                        
226                        this.crv = crv;
227                        
228                        if (x == null) {
229                                throw new IllegalArgumentException("The 'x' coordinate must not be null");
230                        }
231                        
232                        this.x = x;
233                }
234                
235                
236                /**
237                 * Creates a new Octet Key Pair JWK builder.
238                 *
239                 * @param okpJWK The Octet Key Pair to start with. Must not be
240                 *              {@code null}.
241                 */
242                public Builder(final OctetKeyPair okpJWK) {
243                        
244                        crv = okpJWK.crv;
245                        x = okpJWK.x;
246                        d = okpJWK.d;
247                        use = okpJWK.getKeyUse();
248                        ops = okpJWK.getKeyOperations();
249                        alg = okpJWK.getAlgorithm();
250                        kid = okpJWK.getKeyID();
251                        x5u = okpJWK.getX509CertURL();
252                        x5t = okpJWK.getX509CertThumbprint();
253                        x5t256 = okpJWK.getX509CertSHA256Thumbprint();
254                        x5c = okpJWK.getX509CertChain();
255                        exp = okpJWK.getExpirationTime();
256                        nbf = okpJWK.getNotBeforeTime();
257                        iat = okpJWK.getIssueTime();
258                        ks = okpJWK.getKeyStore();
259                }
260                
261                
262                /**
263                 * Sets the private 'd' parameter.
264                 *
265                 * @param d The private 'd' parameter, {@code null} if not 
266                 *          specified (for a public key).
267                 *
268                 * @return This builder.
269                 */
270                public Builder d(final Base64URL d) {
271                        
272                        this.d = d;
273                        return this;
274                }
275                
276                
277                /**
278                 * Sets the use ({@code use}) of the JWK.
279                 *
280                 * @param use The key use, {@code null} if not specified or if
281                 *            the key is intended for signing as well as
282                 *            encryption.
283                 *
284                 * @return This builder.
285                 */
286                public Builder keyUse(final KeyUse use) {
287                        
288                        this.use = use;
289                        return this;
290                }
291                
292                
293                /**
294                 * Sets the operations ({@code key_ops}) of the JWK.
295                 *
296                 * @param ops The key operations, {@code null} if not
297                 *            specified.
298                 *
299                 * @return This builder.
300                 */
301                public Builder keyOperations(final Set<KeyOperation> ops) {
302                        
303                        this.ops = ops;
304                        return this;
305                }
306                
307                
308                /**
309                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
310                 *
311                 * @param alg The intended JOSE algorithm, {@code null} if not
312                 *            specified.
313                 *
314                 * @return This builder.
315                 */
316                public Builder algorithm(final Algorithm alg) {
317                        
318                        this.alg = alg;
319                        return this;
320                }
321                
322                /**
323                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used
324                 * to match a specific key. This can be used, for instance, to
325                 * choose a key within a {@link JWKSet} during key rollover.
326                 * The key ID may also correspond to a JWS/JWE {@code kid}
327                 * header parameter value.
328                 *
329                 * @param kid The key ID, {@code null} if not specified.
330                 *
331                 * @return This builder.
332                 */
333                public Builder keyID(final String kid) {
334                        
335                        this.kid = kid;
336                        return this;
337                }
338                
339                
340                /**
341                 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK
342                 * thumbprint (RFC 7638). The key ID can be used to match a
343                 * specific key. This can be used, for instance, to choose a
344                 * key within a {@link JWKSet} during key rollover. The key ID
345                 * may also correspond to a JWS/JWE {@code kid} header
346                 * parameter value.
347                 *
348                 * @return This builder.
349                 *
350                 * @throws JOSEException If the SHA-256 hash algorithm is not
351                 *                       supported.
352                 */
353                public Builder keyIDFromThumbprint()
354                        throws JOSEException {
355                        
356                        return keyIDFromThumbprint("SHA-256");
357                }
358                
359                
360                /**
361                 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint
362                 * (RFC 7638). The key ID can be used to match a specific key.
363                 * This can be used, for instance, to choose a key within a
364                 * {@link JWKSet} during key rollover. The key ID may also
365                 * correspond to a JWS/JWE {@code kid} header parameter value.
366                 *
367                 * @param hashAlg The hash algorithm for the JWK thumbprint
368                 *                computation. Must not be {@code null}.
369                 *
370                 * @return This builder.
371                 *
372                 * @throws JOSEException If the hash algorithm is not
373                 *                       supported.
374                 */
375                public Builder keyIDFromThumbprint(final String hashAlg)
376                        throws JOSEException {
377                        
378                        // Put mandatory params in sorted order
379                        LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
380                        requiredParams.put(JWKParameterNames.OKP_SUBTYPE, crv.toString());
381                        requiredParams.put(JWKParameterNames.KEY_TYPE, KeyType.OKP.getValue());
382                        requiredParams.put(JWKParameterNames.OKP_PUBLIC_KEY, x.toString());
383                        this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString();
384                        return this;
385                }
386                
387                
388                /**
389                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
390                 *
391                 * @param x5u The X.509 certificate URL, {@code null} if not
392                 *            specified.
393                 *
394                 * @return This builder.
395                 */
396                public Builder x509CertURL(final URI x5u) {
397                        
398                        this.x5u = x5u;
399                        return this;
400                }
401                
402                
403                /**
404                 * Sets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of
405                 * the JWK.
406                 *
407                 * @param x5t The X.509 certificate SHA-1 thumbprint,
408                 *            {@code null} if not specified.
409                 *
410                 * @return This builder.
411                 */
412                @Deprecated
413                public Builder x509CertThumbprint(final Base64URL x5t) {
414                        
415                        this.x5t = x5t;
416                        return this;
417                }
418                
419                
420                /**
421                 * Sets the X.509 certificate SHA-256 thumbprint
422                 * ({@code x5t#S256}) of the JWK.
423                 *
424                 * @param x5t256 The X.509 certificate SHA-256 thumbprint,
425                 *               {@code null} if not specified.
426                 *
427                 * @return This builder.
428                 */
429                public Builder x509CertSHA256Thumbprint(final Base64URL x5t256) {
430                        
431                        this.x5t256 = x5t256;
432                        return this;
433                }
434                
435                
436                /**
437                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
438                 *
439                 * @param x5c The X.509 certificate chain as a unmodifiable
440                 *            list, {@code null} if not specified.
441                 *
442                 * @return This builder.
443                 */
444                public Builder x509CertChain(final List<Base64> x5c) {
445                        
446                        this.x5c = x5c;
447                        return this;
448                }
449                
450                
451                /**
452                 * Sets the expiration time ({@code exp}) of the JWK.
453                 *
454                 * @param exp The expiration time, {@code null} if not
455                 *            specified.
456                 *
457                 * @return This builder.
458                 */
459                public Builder expirationTime(final Date exp) {
460                        
461                        this.exp = exp;
462                        return this;
463                }
464                
465                
466                /**
467                 * Sets the not-before time ({@code nbf}) of the JWK.
468                 *
469                 * @param nbf The not-before time, {@code null} if not
470                 *            specified.
471                 *
472                 * @return This builder.
473                 */
474                public Builder notBeforeTime(final Date nbf) {
475                        
476                        this.nbf = nbf;
477                        return this;
478                }
479                
480                
481                /**
482                 * Sets the issued-at time ({@code iat}) of the JWK.
483                 *
484                 * @param iat The issued-at time, {@code null} if not
485                 *            specified.
486                 *
487                 * @return This builder.
488                 */
489                public Builder issueTime(final Date iat) {
490                        
491                        this.iat = iat;
492                        return this;
493                }
494                
495                
496                /**
497                 * Sets the underlying key store.
498                 *
499                 * @param keyStore Reference to the underlying key store,
500                 *                 {@code null} if none.
501                 *
502                 * @return This builder.
503                 */
504                public Builder keyStore(final KeyStore keyStore) {
505                        
506                        this.ks = keyStore;
507                        return this;
508                }
509                
510                
511                /**
512                 * Builds a new Octet Key Pair JWK.
513                 *
514                 * @return The Octet Key Pair JWK.
515                 *
516                 * @throws IllegalStateException If the JWK parameters were
517                 *                               inconsistently specified.
518                 */
519                public OctetKeyPair build() {
520                        
521                        try {
522                                if (d == null) {
523                                        // Public key
524                                        return new OctetKeyPair(crv, x, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, ks);
525                                }
526                                
527                                // Public / private key pair with 'd'
528                                return new OctetKeyPair(crv, x, d, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, ks);
529                                
530                        } catch (IllegalArgumentException e) {
531                                throw new IllegalStateException(e.getMessage(), e);
532                        }
533                }
534        }
535        
536        
537        /**
538         * The curve name.
539         */
540        private final Curve crv;
541        
542        
543        /**
544         * The public 'x' parameter.
545         */
546        private final Base64URL x;
547
548
549        /**
550         * The public 'x' parameter, decoded from Base64.
551         * Cached for performance and to reduce the risk of side channel attacks
552         * against the Base64 decoding procedure.
553         */
554        private final byte[] decodedX;
555
556
557        /**
558         * The private 'd' parameter.
559         */
560        private final Base64URL d;
561
562
563        /**
564         * The private 'd' parameter, decoded from Base64.
565         * Cached for performance and to reduce the risk of side channel attacks
566         * against the Base64 decoding procedure.
567         */
568        private final byte[] decodedD;
569
570
571        /**
572         * Creates a new public Octet Key Pair JSON Web Key (JWK) with the
573         * specified parameters.
574         *
575         * @param crv    The cryptographic curve. Must not be {@code null}.
576         * @param x      The public 'x' parameter. Must not be {@code null}.
577         * @param use    The key use, {@code null} if not specified or if the
578         *               key is intended for signing as well as encryption.
579         * @param ops    The key operations, {@code null} if not specified.
580         * @param alg    The intended JOSE algorithm for the key, {@code null}
581         *               if not specified.
582         * @param kid    The key ID, {@code null} if not specified.
583         * @param x5u    The X.509 certificate URL, {@code null} if not
584         *               specified.
585         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
586         *               if not specified.
587         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
588         *               if not specified.
589         * @param x5c    The X.509 certificate chain, {@code null} if not
590         *               specified.
591         * @param ks     Reference to the underlying key store, {@code null} if
592         *               not specified.
593         */
594        @Deprecated
595        public OctetKeyPair(final Curve crv, final Base64URL x,
596                            final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
597                            final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
598                            final KeyStore ks) {
599                
600                this(crv, x, use, ops, alg, kid, x5u, x5t, x5t256, x5c, null, null, null, ks);
601        }
602        
603        
604        /**
605         * Creates a new public / private Octet Key Pair JSON Web Key (JWK)
606         * with the specified parameters.
607         *
608         * @param crv    The cryptographic curve. Must not be {@code null}.
609         * @param x      The public 'x' parameter. Must not be {@code null}.
610         * @param d      The private 'd' parameter. Must not be {@code null}.
611         * @param use    The key use, {@code null} if not specified or if the
612         *               key is intended for signing as well as encryption.
613         * @param ops    The key operations, {@code null} if not specified.
614         * @param alg    The intended JOSE algorithm for the key, {@code null}
615         *               if not specified.
616         * @param kid    The key ID, {@code null} if not specified.
617         * @param x5u    The X.509 certificate URL, {@code null} if not
618         *               specified.
619         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
620         *               if not specified.
621         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
622         *               if not specified.
623         * @param x5c    The X.509 certificate chain, {@code null} if not
624         *               specified.
625         * @param ks     Reference to the underlying key store, {@code null} if
626         *               not specified.
627         */
628        @Deprecated
629        public OctetKeyPair(final Curve crv, final Base64URL x, final Base64URL d,
630                            final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
631                            final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
632                            final KeyStore ks) {
633                
634                this(crv, x, d, use, ops, alg, kid, x5u, x5t, x5t256, x5c, null, null, null, ks);
635        }
636
637
638        /**
639         * Creates a new public Octet Key Pair JSON Web Key (JWK) with the
640         * specified parameters.
641         *
642         * @param crv    The cryptographic curve. Must not be {@code null}.
643         * @param x      The public 'x' parameter. Must not be {@code null}.
644         * @param use    The key use, {@code null} if not specified or if the
645         *               key is intended for signing as well as encryption.
646         * @param ops    The key operations, {@code null} if not specified.
647         * @param alg    The intended JOSE algorithm for the key, {@code null}
648         *               if not specified.
649         * @param kid    The key ID, {@code null} if not specified.
650         * @param x5u    The X.509 certificate URL, {@code null} if not
651         *               specified.
652         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
653         *               if not specified.
654         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
655         *               if not specified.
656         * @param x5c    The X.509 certificate chain, {@code null} if not
657         *               specified.
658         * @param exp    The key expiration time, {@code null} if not
659         *               specified.
660         * @param nbf    The key not-before time, {@code null} if not
661         *               specified.
662         * @param iat    The key issued-at time, {@code null} if not specified.
663         * @param ks     Reference to the underlying key store, {@code null} if
664         *               not specified.
665         */
666        public OctetKeyPair(final Curve crv, final Base64URL x,
667                            final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
668                            final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
669                            final Date exp, final Date nbf, final Date iat,
670                            final KeyStore ks) {
671                
672                super(KeyType.OKP, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, ks);
673                
674                if (crv == null) {
675                        throw new IllegalArgumentException("The curve must not be null");
676                }
677                
678                if (! SUPPORTED_CURVES.contains(crv)) {
679                        throw new IllegalArgumentException("Unknown / unsupported curve: " + crv);
680                }
681                
682                this.crv = crv;
683                
684                if (x == null) {
685                        throw new IllegalArgumentException("The '" + JWKParameterNames.OKP_PUBLIC_KEY + "' parameter must not be null");
686                }
687                
688                this.x = x;
689                decodedX = x.decode();
690                
691                d = null;
692                decodedD = null;
693        }
694        
695        
696        /**
697         * Creates a new public / private Octet Key Pair JSON Web Key (JWK)
698         * with the specified parameters.
699         *
700         * @param crv    The cryptographic curve. Must not be {@code null}.
701         * @param x      The public 'x' parameter. Must not be {@code null}.
702         * @param d      The private 'd' parameter. Must not be {@code null}.
703         * @param use    The key use, {@code null} if not specified or if the
704         *               key is intended for signing as well as encryption.
705         * @param ops    The key operations, {@code null} if not specified.
706         * @param alg    The intended JOSE algorithm for the key, {@code null}
707         *               if not specified.
708         * @param kid    The key ID, {@code null} if not specified.
709         * @param x5u    The X.509 certificate URL, {@code null} if not
710         *               specified.
711         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
712         *               if not specified.
713         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
714         *               if not specified.
715         * @param x5c    The X.509 certificate chain, {@code null} if not
716         *               specified.
717         * @param exp    The key expiration time, {@code null} if not
718         *               specified.
719         * @param nbf    The key not-before time, {@code null} if not
720         *               specified.
721         * @param iat    The key issued-at time, {@code null} if not specified.
722         * @param ks     Reference to the underlying key store, {@code null} if
723         *               not specified.
724         */
725        public OctetKeyPair(final Curve crv, final Base64URL x, final Base64URL d,
726                            final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
727                            final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
728                            final Date exp, final Date nbf, final Date iat,
729                            final KeyStore ks) {
730                
731                super(KeyType.OKP, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, ks);
732                
733                if (crv == null) {
734                        throw new IllegalArgumentException("The curve must not be null");
735                }
736                
737                if (! SUPPORTED_CURVES.contains(crv)) {
738                        throw new IllegalArgumentException("Unknown / unsupported curve: " + crv);
739                }
740                
741                this.crv = crv;
742                
743                if (x == null) {
744                        throw new IllegalArgumentException("The '" + JWKParameterNames.OKP_PUBLIC_KEY + "' parameter must not be null");
745                }
746                
747                this.x = x;
748                decodedX = x.decode();
749                
750                if (d == null) {
751                        throw new IllegalArgumentException("The '" + JWKParameterNames.OKP_PRIVATE_KEY + "' parameter must not be null");
752                }
753                
754                this.d = d;
755                decodedD = d.decode();
756        }
757        
758        
759        @Override
760        public Curve getCurve() {
761                
762                return crv;
763        }
764        
765        
766        /**
767         * Gets the public 'x' parameter.
768         *
769         * @return The public 'x' parameter.
770         */
771        public Base64URL getX() {
772                
773                return x;
774        }
775
776
777        /**
778         * Gets the public 'x' parameter, decoded from Base64.
779         *
780         * @return The public 'x' parameter in bytes.
781         */
782        public byte[] getDecodedX() {
783
784                return decodedX.clone();
785        }
786
787
788        /**
789         * Gets the private 'd' parameter.
790         *
791         * @return The private 'd' coordinate, {@code null} if not specified
792         *         (for a public key).
793         */
794        public Base64URL getD() {
795                
796                return d;
797        }
798
799
800        /**
801         * Gets the private 'd' parameter, decoded from Base64.
802         *
803         * @return The private 'd' coordinate in bytes, {@code null} if not specified
804         *         (for a public key).
805         */
806        public byte[] getDecodedD() {
807
808                return decodedD == null ? null : decodedD.clone();
809        }
810
811
812        @Override
813        public PublicKey toPublicKey()
814                throws JOSEException {
815                
816                throw new JOSEException("Export to java.security.PublicKey not supported");
817        }
818        
819        
820        @Override
821        public PrivateKey toPrivateKey()
822                throws JOSEException {
823                
824                throw new JOSEException("Export to java.security.PrivateKey not supported");
825        }
826        
827        
828        @Override
829        public KeyPair toKeyPair()
830                throws JOSEException {
831                
832                throw new JOSEException("Export to java.security.KeyPair not supported");
833        }
834        
835        
836        @Override
837        public boolean matches(final X509Certificate cert) {
838                // X.509 certs don't support OKP yet
839                return false;
840        }
841        
842        
843        @Override
844        public LinkedHashMap<String,?> getRequiredParams() {
845                
846                // Put mandatory params in sorted order
847                LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
848                requiredParams.put(JWKParameterNames.OKP_SUBTYPE, crv.toString());
849                requiredParams.put(JWKParameterNames.KEY_TYPE, getKeyType().getValue());
850                requiredParams.put(JWKParameterNames.OKP_PUBLIC_KEY, x.toString());
851                return requiredParams;
852        }
853        
854        
855        @Override
856        public boolean isPrivate() {
857                
858                return d != null;
859        }
860        
861        
862        /**
863         * Returns a copy of this Octet Key Pair JWK with any private values
864         * removed.
865         *
866         * @return The copied public Octet Key Pair JWK.
867         */
868        @Override
869        public OctetKeyPair toPublicJWK() {
870                
871                return new OctetKeyPair(
872                        getCurve(), getX(),
873                        getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(),
874                        getX509CertURL(), getX509CertThumbprint(), getX509CertSHA256Thumbprint(), getX509CertChain(),
875                        getExpirationTime(), getNotBeforeTime(), getIssueTime(),
876                        getKeyStore());
877        }
878        
879        
880        @Override
881        public Map<String, Object> toJSONObject() {
882                
883                Map<String, Object> o = super.toJSONObject();
884                
885                // Append OKP specific attributes
886                o.put(JWKParameterNames.OKP_SUBTYPE, crv.toString());
887                o.put(JWKParameterNames.OKP_PUBLIC_KEY, x.toString());
888                
889                if (d != null) {
890                        o.put(JWKParameterNames.OKP_PRIVATE_KEY, d.toString());
891                }
892                
893                return o;
894        }
895        
896        
897        @Override
898        public int size() {
899                
900                return ByteUtils.bitLength(x.decode());
901        }
902        
903        
904        /**
905         * Parses a public / private Octet Key Pair JWK from the specified JSON
906         * object string representation.
907         *
908         * @param s The JSON object string to parse. Must not be {@code null}.
909         *
910         * @return The public / private Octet Key Pair JWK.
911         *
912         * @throws ParseException If the string couldn't be parsed to an Octet
913         *                        Key Pair JWK.
914         */
915        public static OctetKeyPair parse(final String s)
916                throws ParseException {
917                
918                return parse(JSONObjectUtils.parse(s));
919        }
920        
921        
922        /**
923         * Parses a public / private Octet Key Pair JWK from the specified JSON
924         * object representation.
925         *
926         * @param jsonObject The JSON object to parse. Must not be
927         *                   {@code null}.
928         *
929         * @return The public / private Octet Key Pair JWK.
930         *
931         * @throws ParseException If the JSON object couldn't be parsed to an
932         *                        Octet Key Pair JWK.
933         */
934        public static OctetKeyPair parse(final Map<String, Object> jsonObject)
935                throws ParseException {
936                
937                // Check the key type
938                if (! KeyType.OKP.equals(JWKMetadata.parseKeyType(jsonObject))) {
939                        throw new ParseException("The key type " + JWKParameterNames.KEY_TYPE + " must be " + KeyType.OKP.getValue(), 0);
940                }
941                
942                // Parse the mandatory parameters
943                Curve crv;
944                try {
945                        crv = Curve.parse(JSONObjectUtils.getString(jsonObject, JWKParameterNames.OKP_SUBTYPE));
946                } catch (IllegalArgumentException e) {
947                        throw new ParseException(e.getMessage(), 0);
948                }
949                
950                Base64URL x = JSONObjectUtils.getBase64URL(jsonObject, JWKParameterNames.OKP_PUBLIC_KEY);
951                
952                // Get the optional private key
953                Base64URL d = JSONObjectUtils.getBase64URL(jsonObject, JWKParameterNames.OKP_PRIVATE_KEY);
954                
955                try {
956                        if (d == null) {
957                                // Public key
958                                return new OctetKeyPair(crv, x,
959                                        JWKMetadata.parseKeyUse(jsonObject),
960                                        JWKMetadata.parseKeyOperations(jsonObject),
961                                        JWKMetadata.parseAlgorithm(jsonObject),
962                                        JWKMetadata.parseKeyID(jsonObject),
963                                        JWKMetadata.parseX509CertURL(jsonObject),
964                                        JWKMetadata.parseX509CertThumbprint(jsonObject),
965                                        JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject),
966                                        JWKMetadata.parseX509CertChain(jsonObject),
967                                        JWKMetadata.parseExpirationTime(jsonObject),
968                                        JWKMetadata.parseNotBeforeTime(jsonObject),
969                                        JWKMetadata.parseIssueTime(jsonObject),
970                                        null);
971                                
972                        } else {
973                                // Key pair
974                                return new OctetKeyPair(crv, x, d,
975                                        JWKMetadata.parseKeyUse(jsonObject),
976                                        JWKMetadata.parseKeyOperations(jsonObject),
977                                        JWKMetadata.parseAlgorithm(jsonObject),
978                                        JWKMetadata.parseKeyID(jsonObject),
979                                        JWKMetadata.parseX509CertURL(jsonObject),
980                                        JWKMetadata.parseX509CertThumbprint(jsonObject),
981                                        JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject),
982                                        JWKMetadata.parseX509CertChain(jsonObject),
983                                        JWKMetadata.parseExpirationTime(jsonObject),
984                                        JWKMetadata.parseNotBeforeTime(jsonObject),
985                                        JWKMetadata.parseIssueTime(jsonObject),
986                                        null);
987                        }
988                        
989                } catch (IllegalArgumentException ex) {
990                        
991                        // Conflicting 'use' and 'key_ops'
992                        throw new ParseException(ex.getMessage(), 0);
993                }
994        }
995
996        
997        @Override
998        public boolean equals(Object o) {
999                if (this == o) return true;
1000                if (!(o instanceof OctetKeyPair)) return false;
1001                if (!super.equals(o)) return false;
1002                OctetKeyPair that = (OctetKeyPair) o;
1003                return Objects.equals(crv, that.crv) &&
1004                                Objects.equals(x, that.x) &&
1005                                Arrays.equals(decodedX, that.decodedX) &&
1006                                Objects.equals(d, that.d) &&
1007                                Arrays.equals(decodedD, that.decodedD);
1008        }
1009
1010        
1011        @Override
1012        public int hashCode() {
1013                int result = Objects.hash(super.hashCode(), crv, x, d);
1014                result = 31 * result + Arrays.hashCode(decodedX);
1015                result = 31 * result + Arrays.hashCode(decodedD);
1016                return result;
1017        }
1018}