001package com.nimbusds.jose.jwk;
002
003
004import java.math.BigInteger;
005import java.net.URI;
006import java.security.Provider;
007import java.util.List;
008import java.security.KeyFactory;
009import java.security.KeyPair;
010import java.security.NoSuchAlgorithmException;
011import java.security.interfaces.ECPrivateKey;
012import java.security.interfaces.ECPublicKey;
013import java.security.spec.ECParameterSpec;
014import java.security.spec.ECPoint;
015import java.security.spec.ECPrivateKeySpec;
016import java.security.spec.ECPublicKeySpec;
017import java.security.spec.InvalidKeySpecException;
018import java.text.ParseException;
019import java.util.Set;
020
021import net.jcip.annotations.Immutable;
022
023import net.minidev.json.JSONObject;
024
025import com.nimbusds.jose.Algorithm;
026import com.nimbusds.jose.JOSEException;
027import com.nimbusds.jose.util.*;
028
029
030/**
031 * Public and private {@link KeyType#EC Elliptic Curve} JSON Web Key (JWK). 
032 * Uses the BouncyCastle.org provider for EC key import and export. This class
033 * is immutable.
034 *
035 * <p>Example JSON object representation of a public EC JWK:
036 * 
037 * <pre>
038 * {
039 *   "kty" : "EC",
040 *   "crv" : "P-256",
041 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
042 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
043 *   "use" : "enc",
044 *   "kid" : "1"
045 * }
046 * </pre>
047 *
048 * <p>Example JSON object representation of a public and private EC JWK:
049 *
050 * <pre>
051 * {
052 *   "kty" : "EC",
053 *   "crv" : "P-256",
054 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
055 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
056 *   "d"   : "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE",
057 *   "use" : "enc",
058 *   "kid" : "1"
059 * }
060 * </pre>
061 *
062 * <p>See http://en.wikipedia.org/wiki/Elliptic_curve_cryptography
063 *
064 * @author Vladimir Dzhuvinov
065 * @author Justin Richer
066 * @version 2015-05-20
067 */
068@Immutable
069public final class ECKey extends JWK {
070
071
072        /**
073         * Cryptographic curve. This class is immutable.
074         *
075         * <p>Includes constants for the following standard cryptographic 
076         * curves:
077         *
078         * <ul>
079         *     <li>{@link #P_256}
080         *     <li>{@link #P_384}
081         *     <li>{@link #P_521}
082         * </ul>
083         *
084         * <p>See "Digital Signature Standard (DSS)", FIPS PUB 186-3, June 
085         * 2009, National Institute of Standards and Technology (NIST).
086         */
087        @Immutable
088        public static class Curve {
089
090
091                /**
092                 * P-256 curve (secp256r1, also called prime256v1).
093                 */
094                public static final Curve P_256 = new Curve("P-256", "secp256r1");
095
096
097                /**
098                 * P-384 curve (secp384r1).
099                 */
100                public static final Curve P_384 = new Curve("P-384", "secp384r1");
101
102
103                /**
104                 * P-521 curve (secp521r1).
105                 */
106                public static final Curve P_521 = new Curve("P-521", "secp521r1");
107
108
109                /**
110                 * The JOSE curve name.
111                 */
112                private final String name;
113
114
115                /**
116                 * The standard curve name, {@code null} if not specified.
117                 */
118                private final String stdName;
119
120
121                /**
122                 * Creates a new cryptographic curve with the specified JOSE
123                 * name. A standard curve name is not unspecified.
124                 *
125                 * @param name The JOSE name of the cryptographic curve. Must not be
126                 *             {@code null}.
127                 */
128                public Curve(final String name) {
129
130                        this(name, null);
131                }
132
133
134                /**
135                 * Creates a new cryptographic curve with the specified JOSE
136                 * and standard names.
137                 *
138                 * @param name    The JOSE name of the cryptographic curve. 
139                 *                Must not be {@code null}.
140                 * @param stdName The standard name of the cryptographic curve,
141                 *                {@code null} if not specified.
142                 */
143                public Curve(final String name, final String stdName) {
144
145                        if (name == null) {
146                                throw new IllegalArgumentException("The JOSE cryptographic curve name must not be null");
147                        }
148
149                        this.name = name;
150
151                        this.stdName = stdName;
152                }
153
154
155                /**
156                 * Returns the JOSE name of this cryptographic curve.
157                 *
158                 * @return The JOSE name.
159                 */
160                public String getName() {
161
162                        return name;
163                }
164
165
166                /**
167                 * Returns the standard name of this cryptographic curve.
168                 *
169                 * @return The standard name, {@code null} if not specified.
170                 */
171                public String getStdName() {
172
173                        return stdName;
174                }
175
176
177                /**
178                 * Returns the parameter specification for this cryptographic
179                 * curve.
180                 *
181                 * @return The EC parameter specification, {@code null} if it
182                 *         cannot be determined.
183                 */
184                public ECParameterSpec toECParameterSpec() {
185
186                        return ECParameterTable.get(this);
187                }
188
189
190                /**
191                 * @see #getName
192                 */
193                @Override
194                public String toString() {
195
196                        return getName();
197                }
198
199
200                @Override
201                public boolean equals(final Object object) {
202
203                        return object instanceof Curve &&
204                               this.toString().equals(object.toString());
205                }
206
207
208                /**
209                 * Parses a cryptographic curve from the specified string.
210                 *
211                 * @param s The string to parse. Must not be {@code null} or
212                 *          empty.
213                 *
214                 * @return The cryptographic curve.
215                 */
216                public static Curve parse(final String s) {
217
218                        if (s == null || s.trim().isEmpty()) {
219                                throw new IllegalArgumentException("The cryptographic curve string must not be null or empty");
220                        }
221
222                        if (s.equals(P_256.getName())) {
223                                return P_256;
224
225                        } else if (s.equals(P_384.getName())) {
226                                return P_384;
227
228                        } else if (s.equals(P_521.getName())) {
229                                return P_521;
230
231                        } else {
232                                return new Curve(s);
233                        }
234                }
235
236
237                /**
238                 * Gets the cryptographic curve for the specified standard
239                 * name.
240                 *
241                 * @param stdName The standard curve name. May be {@code null}.
242                 *
243                 * @return The curve, {@code null} if it cannot be determined.
244                 */
245                public static Curve forStdName(final String stdName) {
246                        if( "secp256r1".equals(stdName) || "prime256v1".equals(stdName)) {
247                                 return P_256;
248                        } else if( "secp384r1".equals(stdName) ) {
249                                return P_384;
250                        } else if( "secp521r1".equals(stdName) ) {
251                                return P_521;
252                        } else {
253                                return null;
254                        }
255                }
256
257
258                /**
259                 * Gets the cryptographic curve for the specified parameter
260                 * specification.
261                 *
262                 * @param spec The EC parameter spec. May be {@code null}.
263                 *
264                 * @return The curve, {@code null} if it cannot be determined.
265                 */
266                public static Curve forECParameterSpec(final ECParameterSpec spec) {
267
268                        return ECParameterTable.get(spec);
269                }
270        }
271
272
273        /**
274         * Builder for constructing Elliptic Curve JWKs.
275         *
276         * <p>Example use:
277         *
278         * <pre>
279         * ECKey key = new ECKey.Builder(Curve.P521, x, y).
280         *             d(d).
281         *             algorithm(JWSAlgorithm.ES512).
282         *             keyID("789").
283         *             build();
284         * </pre>
285         */
286        public static class Builder {
287
288
289                /**
290                 * The curve name.
291                 */
292                private final Curve crv;
293
294
295                /**
296                 * The public 'x' EC coordinate.
297                 */
298                private final Base64URL x;
299
300
301                /**
302                 * The public 'y' EC coordinate.
303                 */
304                private final Base64URL y;
305                
306
307                /**
308                 * The private 'd' EC coordinate, optional.
309                 */
310                private Base64URL d;
311
312
313                /**
314                 * The key use, optional.
315                 */
316                private KeyUse use;
317
318
319                /**
320                 * The key operations, optional.
321                 */
322                private Set<KeyOperation> ops;
323
324
325                /**
326                 * The intended JOSE algorithm for the key, optional.
327                 */
328                private Algorithm alg;
329
330
331                /**
332                 * The key ID, optional.
333                 */
334                private String kid;
335
336
337                /**
338                 * X.509 certificate URL, optional.
339                 */
340                private URI x5u;
341
342
343                /**
344                 * X.509 certificate thumbprint, optional.
345                 */
346                private Base64URL x5t;
347
348
349                /**
350                 * The X.509 certificate chain, optional.
351                 */
352                private List<Base64> x5c;
353
354
355                /**
356                 * Creates a new Elliptic Curve JWK builder.
357                 *
358                 * @param crv The cryptographic curve. Must not be 
359                 *            {@code null}.
360                 * @param x   The public 'x' coordinate for the elliptic curve 
361                 *            point. It is represented as the Base64URL 
362                 *            encoding of the coordinate's big endian 
363                 *            representation. Must not be {@code null}.
364                 * @param y   The public 'y' coordinate for the elliptic curve 
365                 *            point. It is represented as the Base64URL 
366                 *            encoding of the coordinate's big endian 
367                 *            representation. Must not be {@code null}.
368                 */
369                public Builder(final Curve crv, final Base64URL x, final Base64URL y) {
370
371                        if (crv == null) {
372                                throw new IllegalArgumentException("The curve must not be null");
373                        }
374
375                        this.crv = crv;
376
377                        if (x == null) {
378                                throw new IllegalArgumentException("The 'x' coordinate must not be null");
379                        }
380
381                        this.x = x;
382
383                        if (y == null) {
384                                throw new IllegalArgumentException("The 'y' coordinate must not be null");
385                        }
386
387                        this.y = y;
388                }
389
390
391                /**
392                 * Creates a new Elliptic Curve JWK builder.
393                 *
394                 * @param crv The cryptographic curve. Must not be 
395                 *            {@code null}.
396                 * @param pub The public EC key to represent. Must not be 
397                 *            {@code null}.
398                 */
399                public Builder(final Curve crv, final ECPublicKey pub) {
400
401                        this(crv,
402                             encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
403                             encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()));
404                }
405
406
407                /**
408                 * Sets the private 'd' coordinate for the elliptic curve 
409                 * point. The alternative method is {@link #privateKey}.
410                 *
411                 * @param d The 'd' coordinate. It is represented as the 
412                 *          Base64URL encoding of the coordinate's big endian 
413                 *          representation. {@code null} if not specified (for
414                 *          a public key).
415                 *
416                 * @return This builder.
417                 */
418                public Builder d(final Base64URL d) {
419
420                        this.d = d;
421                        return this;
422                }
423
424
425                /**
426                 * Sets the private Elliptic Curve key. The alternative method 
427                 * is {@link #d}.
428                 *
429                 * @param priv The private EC key, used to obtain the private
430                 *             'd' coordinate for the elliptic curve point.
431                 *             {@code null} if not specified (for a public 
432                 *             key).
433                 *
434                 * @return This builder.
435                 */
436                public Builder privateKey(final ECPrivateKey priv) {
437
438                        if (priv != null) {
439                                this.d = encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS());
440                        }
441                        
442                        return this;
443                }
444
445
446                /**
447                 * Sets the use ({@code use}) of the JWK.
448                 *
449                 * @param use The key use, {@code null} if not specified or if 
450                 *            the key is intended for signing as well as 
451                 *            encryption.
452                 *
453                 * @return This builder.
454                 */
455                public Builder keyUse(final KeyUse use) {
456
457                        this.use = use;
458                        return this;
459                }
460
461
462                /**
463                 * Sets the operations ({@code key_ops}) of the JWK.
464                 *
465                 * @param ops The key operations, {@code null} if not
466                 *            specified.
467                 *
468                 * @return This builder.
469                 */
470                public Builder keyOperations(final Set<KeyOperation> ops) {
471
472                        this.ops = ops;
473                        return this;
474                }
475
476
477                /**
478                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
479                 *
480                 * @param alg The intended JOSE algorithm, {@code null} if not 
481                 *            specified.
482                 *
483                 * @return This builder.
484                 */
485                public Builder algorithm(final Algorithm alg) {
486
487                        this.alg = alg;
488                        return this;
489                }
490
491                /**
492                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 
493                 * to match a specific key. This can be used, for instance, to 
494                 * choose a key within a {@link JWKSet} during key rollover. 
495                 * The key ID may also correspond to a JWS/JWE {@code kid} 
496                 * header parameter value.
497                 *
498                 * @param kid The key ID, {@code null} if not specified.
499                 *
500                 * @return This builder.
501                 */
502                public Builder keyID(final String kid) {
503
504                        this.kid = kid;
505                        return this;
506                }
507
508
509                /**
510                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
511                 *
512                 * @param x5u The X.509 certificate URL, {@code null} if not 
513                 *            specified.
514                 *
515                 * @return This builder.
516                 */
517                public Builder x509CertURL(final URI x5u) {
518
519                        this.x5u = x5u;
520                        return this;
521                }
522
523
524                /**
525                 * Sets the X.509 certificate thumbprint ({@code x5t}) of the
526                 * JWK.
527                 *
528                 * @param x5t The X.509 certificate thumbprint, {@code null} if 
529                 *            not specified.
530                 *
531                 * @return This builder.
532                 */
533                public Builder x509CertThumbprint(final Base64URL x5t) {
534
535                        this.x5t = x5t;
536                        return this;
537                }
538
539
540                /**
541                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
542                 *
543                 * @param x5c The X.509 certificate chain as a unmodifiable 
544                 *            list, {@code null} if not specified.
545                 *
546                 * @return This builder.
547                 */
548                public Builder x509CertChain(final List<Base64> x5c) {
549
550                        this.x5c = x5c;
551                        return this;
552                }
553
554
555                /**
556                 * Builds a new octet sequence JWK.
557                 *
558                 * @return The octet sequence JWK.
559                 *
560                 * @throws IllegalStateException If the JWK parameters were
561                 *                               inconsistently specified.
562                 */
563                public ECKey build() {
564
565                        try {
566                                if (d == null) {
567                                        // Public key
568                                        return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5c);
569                                }
570
571                                // Pair
572                                return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5c);
573
574                        } catch (IllegalArgumentException e) {
575
576                                throw new IllegalStateException(e.getMessage(), e);
577                        }
578                }
579        }
580
581
582        /**
583         * Returns the Base64URL encoding of the specified elliptic curve 'x',
584         * 'y' or 'd' coordinate, with leading zero padding up to the specified
585         * field size in bits.
586         *
587         * @param fieldSize  The field size in bits.
588         * @param coordinate The elliptic curve coordinate. Must not be
589         *                   {@code null}.
590         *
591         * @return The Base64URL-encoded coordinate, with leading zero padding
592         *         up to the curve's field size.
593         */
594        public static Base64URL encodeCoordinate(final int fieldSize, final BigInteger coordinate) {
595
596                byte[] unpadded = BigIntegerUtils.toBytesUnsigned(coordinate);
597
598                int bytesToOutput = (fieldSize + 7)/8;
599
600                if (unpadded.length >= bytesToOutput) {
601                        // Greater-than check to prevent exception on malformed
602                        // key below
603                        return Base64URL.encode(unpadded);
604                }
605
606                byte[] padded = new byte[bytesToOutput];
607
608                System.arraycopy(unpadded, 0, padded, bytesToOutput - unpadded.length, unpadded.length);
609
610                return Base64URL.encode(padded);
611        }
612
613
614        /**
615         * The curve name.
616         */
617        private final Curve crv;
618
619
620        /**
621         * The public 'x' EC coordinate.
622         */
623        private final Base64URL x;
624
625
626        /**
627         * The public 'y' EC coordinate.
628         */
629        private final Base64URL y;
630        
631
632        /**
633         * The private 'd' EC coordinate
634         */
635        private final Base64URL d;
636
637
638        /**
639         * Creates a new public Elliptic Curve 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' coordinate for the elliptic curve point.
644         *            It is represented as the Base64URL encoding of the 
645         *            coordinate's big endian representation. Must not be 
646         *            {@code null}.
647         * @param y   The public 'y' coordinate for the elliptic curve point. 
648         *            It is represented as the Base64URL encoding of the 
649         *            coordinate's big endian representation. Must not be 
650         *            {@code null}.
651         * @param use The key use, {@code null} if not specified or if the key
652         *            is intended for signing as well as encryption.
653         * @param ops The key operations, {@code null} if not specified.
654         * @param alg The intended JOSE algorithm for the key, {@code null} if
655         *            not specified.
656         * @param kid The key ID, {@code null} if not specified.
657         * @param x5u The X.509 certificate URL, {@code null} if not specified.
658         * @param x5t The X.509 certificate thumbprint, {@code null} if not
659         *            specified.
660         * @param x5c The X.509 certificate chain, {@code null} if not 
661         *            specified.
662         */
663        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 
664                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
665                     final URI x5u, final Base64URL x5t, final List<Base64> x5c) {
666
667                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c);
668
669                if (crv == null) {
670                        throw new IllegalArgumentException("The curve must not be null");
671                }
672
673                this.crv = crv;
674
675                if (x == null) {
676                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
677                }
678
679                this.x = x;
680
681                if (y == null) {
682                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
683                }
684
685                this.y = y;
686
687                this.d = null;
688        }
689
690
691        /**
692         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
693         * with the specified parameters.
694         *
695         * @param crv The cryptographic curve. Must not be {@code null}.
696         * @param x   The public 'x' coordinate for the elliptic curve point.
697         *            It is represented as the Base64URL encoding of the 
698         *            coordinate's big endian representation. Must not be 
699         *            {@code null}.
700         * @param y   The public 'y' coordinate for the elliptic curve point. 
701         *            It is represented as the Base64URL encoding of the 
702         *            coordinate's big endian representation. Must not be 
703         *            {@code null}.
704         * @param d   The private 'd' coordinate for the elliptic curve point. 
705         *            It is represented as the Base64URL encoding of the 
706         *            coordinate's big endian representation. Must not be 
707         *            {@code null}.
708         * @param use The key use, {@code null} if not specified or if the key
709         *            is intended for signing as well as encryption.
710         * @param ops The key operations, {@code null} if not specified.
711         * @param alg The intended JOSE algorithm for the key, {@code null} if
712         *            not specified.
713         * @param kid The key ID, {@code null} if not specified.
714         * @param x5u The X.509 certificate URL, {@code null} if not specified.
715         * @param x5t The X.509 certificate thumbprint, {@code null} if not
716         *            specified.
717         * @param x5c The X.509 certificate chain, {@code null} if not 
718         *            specified.
719         */
720        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final Base64URL d,
721                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
722                     final URI x5u, final Base64URL x5t, final List<Base64> x5c) {
723
724                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c);
725
726                if (crv == null) {
727                        throw new IllegalArgumentException("The curve must not be null");
728                }
729
730                this.crv = crv;
731
732                if (x == null) {
733                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
734                }
735
736                this.x = x;
737
738                if (y == null) {
739                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
740                }
741
742                this.y = y;
743                
744                if (d == null) {
745                        throw new IllegalArgumentException("The 'd' coordinate must not be null");
746                }
747
748                this.d = d;
749        }
750
751
752        /**
753         * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
754         * specified parameters.
755         *
756         * @param crv The cryptographic curve. Must not be {@code null}.
757         * @param pub The public EC key to represent. Must not be {@code null}.
758         * @param use The key use, {@code null} if not specified or if the key
759         *            is intended for signing as well as encryption.
760         * @param ops The key operations, {@code null} if not specified.
761         * @param alg The intended JOSE algorithm for the key, {@code null} if
762         *            not specified.
763         * @param kid The key ID, {@code null} if not specified.
764         * @param x5u The X.509 certificate URL, {@code null} if not specified.
765         * @param x5t The X.509 certificate thumbprint, {@code null} if not
766         *            specified.
767         * @param x5c The X.509 certificate chain, {@code null} if not 
768         *            specified.
769         */
770        public ECKey(final Curve crv, final ECPublicKey pub, 
771                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
772                     final URI x5u, final Base64URL x5t, final List<Base64> x5c) {
773
774                this(crv, 
775                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
776                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
777                     use, ops, alg, kid,
778                     x5u, x5t, x5c);
779        }
780
781
782        /**
783         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
784         * with the specified parameters.
785         *
786         * @param crv  The cryptographic curve. Must not be {@code null}.
787         * @param pub  The public EC key to represent. Must not be 
788         *             {@code null}.
789         * @param priv The private EC key to represent. Must not be 
790         *             {@code null}.
791         * @param use  The key use, {@code null} if not specified or if the key
792         *             is intended for signing as well as encryption.
793         * @param ops  The key operations, {@code null} if not specified.
794         * @param alg  The intended JOSE algorithm for the key, {@code null} if
795         *             not specified.
796         * @param kid  The key ID, {@code null} if not specified.
797         * @param x5u  The X.509 certificate URL, {@code null} if not 
798         *             specified.
799         * @param x5t  The X.509 certificate thumbprint, {@code null} if not
800         *             specified.
801         * @param x5c  The X.509 certificate chain, {@code null} if not 
802         *             specified.
803         */
804        public ECKey(final Curve crv, final ECPublicKey pub, final ECPrivateKey priv, 
805                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
806                     final URI x5u, final Base64URL x5t, final List<Base64> x5c) {
807
808                this(crv,
809                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
810                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
811                     encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()),
812                     use, ops, alg, kid,
813                     x5u, x5t, x5c);
814        }
815
816
817        /**
818         * Gets the cryptographic curve.
819         *
820         * @return The cryptographic curve.
821         */
822        public Curve getCurve() {
823
824                return crv;
825        }
826
827
828        /**
829         * Gets the public 'x' coordinate for the elliptic curve point.
830         *
831         * @return The 'x' coordinate. It is represented as the Base64URL 
832         *         encoding of the coordinate's big endian representation.
833         */
834        public Base64URL getX() {
835
836                return x;
837        }
838
839
840        /**
841         * Gets the public 'y' coordinate for the elliptic curve point.
842         *
843         * @return The 'y' coordinate. It is represented as the Base64URL 
844         *         encoding of the coordinate's big endian representation.
845         */
846        public Base64URL getY() {
847
848                return y;
849        }
850
851        
852        /**
853         * Gets the private 'd' coordinate for the elliptic curve point. It is 
854         * represented as the Base64URL encoding of the coordinate's big endian 
855         * representation.
856         *
857         * @return The 'd' coordinate.  It is represented as the Base64URL 
858         *         encoding of the coordinate's big endian representation. 
859         *         {@code null} if not specified (for a public key).
860         */
861        public Base64URL getD() {
862
863                return d;
864        }
865
866
867        /**
868         * Returns a standard {@code java.security.interfaces.ECPublicKey} 
869         * representation of this Elliptic Curve JWK. Uses the default JCA
870         * provider.
871         * 
872         * @return The public Elliptic Curve key.
873         * 
874         * @throws JOSEException If EC is not supported by the underlying Java
875         *                       Cryptography (JCA) provider or if the JWK
876         *                       parameters are invalid for a public EC key.
877         */
878        public ECPublicKey toECPublicKey()
879                throws JOSEException {
880
881                return toECPublicKey(null);
882        }
883
884
885        /**
886         * Returns a standard {@code java.security.interfaces.ECPublicKey}
887         * representation of this Elliptic Curve JWK.
888         *
889         * @param provider The specific JCA provider to use, {@code null}
890         *                 implies the default one.
891         *
892         * @return The public Elliptic Curve key.
893         *
894         * @throws JOSEException If EC is not supported by the underlying Java
895         *                       Cryptography (JCA) provider or if the JWK
896         *                       parameters are invalid for a public EC key.
897         */
898        public ECPublicKey toECPublicKey(final Provider provider)
899                throws JOSEException {
900
901                ECParameterSpec spec = crv.toECParameterSpec();
902
903                if (spec == null) {
904                        throw new JOSEException("Couldn't get EC parameter spec for curve " + crv);
905                }
906
907                ECPoint w = new ECPoint(x.decodeToBigInteger(), y.decodeToBigInteger());
908
909                ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, spec);
910
911                try {
912                        KeyFactory keyFactory;
913
914                        if (provider == null) {
915                                keyFactory = KeyFactory.getInstance("EC");
916                        } else {
917                                keyFactory = KeyFactory.getInstance("EC", provider);
918                        }
919
920                        return (ECPublicKey) keyFactory.generatePublic(publicKeySpec);
921
922                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
923
924                        throw new JOSEException(e.getMessage(), e);
925                }
926        }
927        
928
929        /**
930         * Returns a standard {@code java.security.interfaces.ECPrivateKey} 
931         * representation of this Elliptic Curve JWK. Uses the default JCA
932         * provider.
933         * 
934         * @return The private Elliptic Curve key, {@code null} if not 
935         *         specified by this JWK.
936         * 
937         * @throws JOSEException If EC is not supported by the underlying Java
938         *                       Cryptography (JCA) provider or if the JWK
939         *                       parameters are invalid for a private EC key.
940         */
941        public ECPrivateKey toECPrivateKey()
942                throws JOSEException {
943
944                return toECPrivateKey(null);
945        }
946
947
948        /**
949         * Returns a standard {@code java.security.interfaces.ECPrivateKey}
950         * representation of this Elliptic Curve JWK.
951         *
952         * @param provider The specific JCA provider to use, {@code null}
953         *                 implies the default one.
954         *
955         * @return The private Elliptic Curve key, {@code null} if not
956         *         specified by this JWK.
957         *
958         * @throws JOSEException If EC is not supported by the underlying Java
959         *                       Cryptography (JCA) provider or if the JWK
960         *                       parameters are invalid for a private EC key.
961         */
962        public ECPrivateKey toECPrivateKey(final Provider provider)
963                throws JOSEException {
964
965                if (d == null) {
966                        // No private 'd' param
967                        return null;
968                }
969
970                ECParameterSpec spec = crv.toECParameterSpec();
971
972                if (spec == null) {
973                        throw new JOSEException("Couldn't get EC parameter spec for curve " + crv);
974                }
975
976                ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d.decodeToBigInteger(), spec);
977
978                try {
979                        KeyFactory keyFactory;
980
981                        if (provider == null) {
982                                keyFactory = KeyFactory.getInstance("EC");
983                        } else {
984                                keyFactory = KeyFactory.getInstance("EC", provider);
985                        }
986
987                        return (ECPrivateKey) keyFactory.generatePrivate(privateKeySpec);
988
989                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
990
991                        throw new JOSEException(e.getMessage(), e);
992                }
993        }
994        
995
996        /**
997         * Returns a standard {@code java.security.KeyPair} representation of 
998         * this Elliptic Curve JWK. Uses the default JCA provider.
999         * 
1000         * @return The Elliptic Curve key pair. The private Elliptic Curve key 
1001         *         will be {@code null} if not specified.
1002         * 
1003         * @throws JOSEException If EC is not supported by the underlying Java
1004         *                       Cryptography (JCA) provider or if the JWK
1005         *                       parameters are invalid for a public and / or
1006         *                       private EC key.
1007         */
1008        public KeyPair toKeyPair()
1009                throws JOSEException {
1010
1011                return toKeyPair(null);
1012        }
1013
1014
1015        /**
1016         * Returns a standard {@code java.security.KeyPair} representation of
1017         * this Elliptic Curve JWK.
1018         *
1019         * @param provider The specific JCA provider to use, {@code null}
1020         *                 implies the default one.
1021         *
1022         * @return The Elliptic Curve key pair. The private Elliptic Curve key
1023         *         will be {@code null} if not specified.
1024         *
1025         * @throws JOSEException If EC is not supported by the underlying Java
1026         *                       Cryptography (JCA) provider or if the JWK
1027         *                       parameters are invalid for a public and / or
1028         *                       private EC key.
1029         */
1030        public KeyPair toKeyPair(final Provider provider)
1031                throws JOSEException {
1032
1033                return new KeyPair(toECPublicKey(provider), toECPrivateKey(provider));
1034        }
1035
1036
1037        @Override
1038        public boolean isPrivate() {
1039
1040                return d != null;
1041        }
1042
1043        
1044        /**
1045         * Returns a copy of this Elliptic Curve JWK with any private values 
1046         * removed.
1047         *
1048         * @return The copied public Elliptic Curve JWK.
1049         */
1050        @Override
1051        public ECKey toPublicJWK() {
1052
1053                return new ECKey(getCurve(), getX(), getY(),
1054                                 getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(),
1055                                 getX509CertURL(), getX509CertThumbprint(), getX509CertChain());
1056        }
1057        
1058
1059        @Override
1060        public JSONObject toJSONObject() {
1061
1062                JSONObject o = super.toJSONObject();
1063
1064                // Append EC specific attributes
1065                o.put("crv", crv.toString());
1066                o.put("x", x.toString());
1067                o.put("y", y.toString());
1068
1069                if (d != null) {
1070                        o.put("d", d.toString());
1071                }
1072                
1073                return o;
1074        }
1075
1076
1077        /**
1078         * Parses a public / private Elliptic Curve JWK from the specified JSON
1079         * object string representation.
1080         *
1081         * @param s The JSON object string to parse. Must not be {@code null}.
1082         *
1083         * @return The public / private Elliptic Curve JWK.
1084         *
1085         * @throws ParseException If the string couldn't be parsed to an
1086         *                        Elliptic Curve JWK.
1087         */
1088        public static ECKey parse(final String s)
1089                throws ParseException {
1090
1091                return parse(JSONObjectUtils.parseJSONObject(s));
1092        }
1093
1094
1095        /**
1096         * Parses a public / private Elliptic Curve JWK from the specified JSON
1097         * object representation.
1098         *
1099         * @param jsonObject The JSON object to parse. Must not be 
1100         *                   {@code null}.
1101         *
1102         * @return The public / private Elliptic Curve JWK.
1103         *
1104         * @throws ParseException If the JSON object couldn't be parsed to an 
1105         *                        Elliptic Curve JWK.
1106         */
1107        public static ECKey parse(final JSONObject jsonObject)
1108                throws ParseException {
1109
1110                // Parse the mandatory parameters first
1111                Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv"));
1112                Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x"));
1113                Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y"));
1114
1115                // Check key type
1116                KeyType kty = JWKMetadata.parseKeyType(jsonObject);
1117
1118                if (kty != KeyType.EC) {
1119                        throw new ParseException("The key type \"kty\" must be EC", 0);
1120                }
1121
1122                // Get optional private key
1123                Base64URL d = null;
1124                if (jsonObject.get("d") != null) {
1125                        d = new Base64URL(JSONObjectUtils.getString(jsonObject, "d"));
1126                }
1127
1128
1129                try {
1130                        if (d == null) {
1131                                // Public key
1132                                return new ECKey(crv, x, y,
1133                                        JWKMetadata.parseKeyUse(jsonObject),
1134                                        JWKMetadata.parseKeyOperations(jsonObject),
1135                                        JWKMetadata.parseAlgorithm(jsonObject),
1136                                        JWKMetadata.parseKeyID(jsonObject),
1137                                        JWKMetadata.parseX509CertURL(jsonObject),
1138                                        JWKMetadata.parseX509CertThumbprint(jsonObject),
1139                                        JWKMetadata.parseX509CertChain(jsonObject));
1140
1141                        } else {
1142                                // Key pair
1143                                return new ECKey(crv, x, y, d,
1144                                        JWKMetadata.parseKeyUse(jsonObject),
1145                                        JWKMetadata.parseKeyOperations(jsonObject),
1146                                        JWKMetadata.parseAlgorithm(jsonObject),
1147                                        JWKMetadata.parseKeyID(jsonObject),
1148                                        JWKMetadata.parseX509CertURL(jsonObject),
1149                                        JWKMetadata.parseX509CertThumbprint(jsonObject),
1150                                        JWKMetadata.parseX509CertChain(jsonObject));
1151                        }
1152
1153                } catch (IllegalArgumentException ex) {
1154
1155                        // Conflicting 'use' and 'key_ops'
1156                        throw new ParseException(ex.getMessage(), 0);
1157                }
1158        }
1159}