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