001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.jwk;
019
020
021import java.math.BigInteger;
022import java.net.URI;
023import java.security.*;
024import java.security.cert.Certificate;
025import java.security.cert.CertificateEncodingException;
026import java.security.cert.X509Certificate;
027import java.security.interfaces.ECPrivateKey;
028import java.security.interfaces.ECPublicKey;
029import java.security.spec.*;
030import java.text.ParseException;
031import java.util.*;
032
033import net.jcip.annotations.Immutable;
034import net.minidev.json.JSONObject;
035import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
036
037import com.nimbusds.jose.Algorithm;
038import com.nimbusds.jose.JOSEException;
039import com.nimbusds.jose.crypto.utils.ECChecks;
040import com.nimbusds.jose.util.Base64;
041import com.nimbusds.jose.util.Base64URL;
042import com.nimbusds.jose.util.BigIntegerUtils;
043import com.nimbusds.jose.util.JSONObjectUtils;
044
045
046/**
047 * Public and private {@link KeyType#EC Elliptic Curve} JSON Web Key (JWK). 
048 * This class is immutable.
049 *
050 * <p>Supported curves:
051 *
052 * <ul>
053 *     <li>{@link Curve#P_256 P-256}
054 *     <li>{@link Curve#SECP256K1 secp256k1}
055 *     <li>{@link Curve#P_384 P-384}
056 *     <li>{@link Curve#P_521 P-512}
057 * </ul>
058 *
059 * <p>Provides EC JWK import from / export to the following standard Java
060 * interfaces and classes:
061 *
062 * <ul>
063 *     <li>{@link java.security.interfaces.ECPublicKey}
064 *     <li>{@link java.security.interfaces.ECPrivateKey}
065 *     <li>{@link java.security.PrivateKey} for an EC key in a PKCS#11 store
066 *     <li>{@link java.security.KeyPair}
067 * </ul>
068 *
069 * <p>Example JSON object representation of a public EC JWK:
070 * 
071 * <pre>
072 * {
073 *   "kty" : "EC",
074 *   "crv" : "P-256",
075 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
076 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
077 *   "use" : "enc",
078 *   "kid" : "1"
079 * }
080 * </pre>
081 *
082 * <p>Example JSON object representation of a private EC JWK:
083 *
084 * <pre>
085 * {
086 *   "kty" : "EC",
087 *   "crv" : "P-256",
088 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
089 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
090 *   "d"   : "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE",
091 *   "use" : "enc",
092 *   "kid" : "1"
093 * }
094 * </pre>
095 *
096 * <p>Use the builder to create a new EC JWK:
097 *
098 * <pre>
099 * ECKey key = new ECKey.Builder(Curve.P_256, x, y)
100 *      .keyUse(KeyUse.SIGNATURE)
101 *      .keyID("1")
102 *      .build();
103 * </pre>
104 *
105 * <p>See http://en.wikipedia.org/wiki/Elliptic_curve_cryptography
106 *
107 * @author Vladimir Dzhuvinov
108 * @author Justin Richer
109 * @version 2019-04-15
110 */
111@Immutable
112public final class ECKey extends JWK implements AsymmetricJWK, CurveBasedJWK {
113
114
115        private static final long serialVersionUID = 1L;
116        
117        
118        /**
119         * Supported EC curves.
120         */
121        public static final Set<Curve> SUPPORTED_CURVES = Collections.unmodifiableSet(
122                new HashSet<>(Arrays.asList(Curve.P_256, Curve.SECP256K1, Curve.P_384, Curve.P_521))
123        );
124
125
126        /**
127         * Builder for constructing Elliptic Curve JWKs.
128         *
129         * <p>Example usage:
130         *
131         * <pre>
132         * ECKey key = new ECKey.Builder(Curve.P521, x, y)
133         *     .d(d)
134         *     .algorithm(JWSAlgorithm.ES512)
135         *     .keyID("1")
136         *     .build();
137         * </pre>
138         */
139        public static class Builder {
140
141
142                /**
143                 * The curve name.
144                 */
145                private final Curve crv;
146
147
148                /**
149                 * The public 'x' EC coordinate.
150                 */
151                private final Base64URL x;
152
153
154                /**
155                 * The public 'y' EC coordinate.
156                 */
157                private final Base64URL y;
158                
159
160                /**
161                 * The private 'd' EC coordinate, optional.
162                 */
163                private Base64URL d;
164                
165                
166                /**
167                 * The private EC key, as PKCS#11 handle, optional.
168                 */
169                private PrivateKey priv;
170
171
172                /**
173                 * The key use, optional.
174                 */
175                private KeyUse use;
176
177
178                /**
179                 * The key operations, optional.
180                 */
181                private Set<KeyOperation> ops;
182
183
184                /**
185                 * The intended JOSE algorithm for the key, optional.
186                 */
187                private Algorithm alg;
188
189
190                /**
191                 * The key ID, optional.
192                 */
193                private String kid;
194
195
196                /**
197                 * X.509 certificate URL, optional.
198                 */
199                private URI x5u;
200
201
202                /**
203                 * X.509 certificate SHA-1 thumbprint, optional.
204                 */
205                @Deprecated
206                private Base64URL x5t;
207                
208                
209                /**
210                 * X.509 certificate SHA-256 thumbprint, optional.
211                 */
212                private Base64URL x5t256;
213
214
215                /**
216                 * The X.509 certificate chain, optional.
217                 */
218                private List<Base64> x5c;
219                
220                
221                /**
222                 * Reference to the underlying key store, {@code null} if none.
223                 */
224                private KeyStore ks;
225
226
227                /**
228                 * Creates a new Elliptic Curve JWK builder.
229                 *
230                 * @param crv The cryptographic curve. Must not be 
231                 *            {@code null}.
232                 * @param x   The public 'x' coordinate for the elliptic curve 
233                 *            point. It is represented as the Base64URL 
234                 *            encoding of the coordinate's big endian 
235                 *            representation. Must not be {@code null}.
236                 * @param y   The public 'y' coordinate for the elliptic curve 
237                 *            point. It is represented as the Base64URL 
238                 *            encoding of the coordinate's big endian 
239                 *            representation. Must not be {@code null}.
240                 */
241                public Builder(final Curve crv, final Base64URL x, final Base64URL y) {
242
243                        if (crv == null) {
244                                throw new IllegalArgumentException("The curve must not be null");
245                        }
246
247                        this.crv = crv;
248
249                        if (x == null) {
250                                throw new IllegalArgumentException("The 'x' coordinate must not be null");
251                        }
252
253                        this.x = x;
254
255                        if (y == null) {
256                                throw new IllegalArgumentException("The 'y' coordinate must not be null");
257                        }
258
259                        this.y = y;
260                }
261
262
263                /**
264                 * Creates a new Elliptic Curve JWK builder.
265                 *
266                 * @param crv The cryptographic curve. Must not be 
267                 *            {@code null}.
268                 * @param pub The public EC key to represent. Must not be 
269                 *            {@code null}.
270                 */
271                public Builder(final Curve crv, final ECPublicKey pub) {
272
273                        this(crv,
274                             encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
275                             encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()));
276                }
277                
278                
279                /**
280                 * Creates a new Elliptic Curve JWK builder.
281                 *
282                 * @param ecJWK The EC JWK to start with. Must not be
283                 *              {@code null}.
284                 */
285                public Builder(final ECKey ecJWK) {
286                        
287                        crv = ecJWK.crv;
288                        x = ecJWK.x;
289                        y = ecJWK.y;
290                        d = ecJWK.d;
291                        priv = ecJWK.privateKey;
292                        use = ecJWK.getKeyUse();
293                        ops = ecJWK.getKeyOperations();
294                        alg = ecJWK.getAlgorithm();
295                        kid = ecJWK.getKeyID();
296                        x5u = ecJWK.getX509CertURL();
297                        x5t = ecJWK.getX509CertThumbprint();
298                        x5t256 = ecJWK.getX509CertSHA256Thumbprint();
299                        x5c = ecJWK.getX509CertChain();
300                        ks = ecJWK.getKeyStore();
301                }
302
303
304                /**
305                 * Sets the private 'd' coordinate for the elliptic curve 
306                 * point. The alternative method is {@link #privateKey}.
307                 *
308                 * @param d The private 'd' coordinate. It is represented as
309                 *          the Base64URL encoding of the coordinate's big
310                 *          endian representation. {@code null} if not
311                 *          specified (for a public key).
312                 *
313                 * @return This builder.
314                 */
315                public Builder d(final Base64URL d) {
316
317                        this.d = d;
318                        return this;
319                }
320
321
322                /**
323                 * Sets the private Elliptic Curve key. The alternative method 
324                 * is {@link #d}.
325                 *
326                 * @param priv The private EC key, used to obtain the private
327                 *             'd' coordinate for the elliptic curve point.
328                 *             {@code null} if not specified (for a public 
329                 *             key).
330                 *
331                 * @return This builder.
332                 */
333                public Builder privateKey(final ECPrivateKey priv) {
334
335                        if (priv != null) {
336                                this.d = encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS());
337                        }
338                        
339                        return this;
340                }
341                
342                
343                /**
344                 * Sets the private EC key, typically for a key located in a
345                 * PKCS#11 store that doesn't expose the private key parameters
346                 * (such as a smart card or HSM).
347                 *
348                 * @param priv The private EC key reference. Its algorithm must
349                 *             be "EC". Must not be {@code null}.
350                 *
351                 * @return This builder.
352                 */
353                public Builder privateKey(final PrivateKey priv) {
354
355                        if (priv instanceof ECPrivateKey) {
356                                return privateKey((ECPrivateKey) priv);
357                        }
358
359                        if (! "EC".equalsIgnoreCase(priv.getAlgorithm())) {
360                                throw new IllegalArgumentException("The private key algorithm must be EC");
361                        }
362                        
363                        this.priv = priv;
364                        return this;
365                }
366
367
368                /**
369                 * Sets the use ({@code use}) of the JWK.
370                 *
371                 * @param use The key use, {@code null} if not specified or if 
372                 *            the key is intended for signing as well as 
373                 *            encryption.
374                 *
375                 * @return This builder.
376                 */
377                public Builder keyUse(final KeyUse use) {
378
379                        this.use = use;
380                        return this;
381                }
382
383
384                /**
385                 * Sets the operations ({@code key_ops}) of the JWK.
386                 *
387                 * @param ops The key operations, {@code null} if not
388                 *            specified.
389                 *
390                 * @return This builder.
391                 */
392                public Builder keyOperations(final Set<KeyOperation> ops) {
393
394                        this.ops = ops;
395                        return this;
396                }
397
398
399                /**
400                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
401                 *
402                 * @param alg The intended JOSE algorithm, {@code null} if not 
403                 *            specified.
404                 *
405                 * @return This builder.
406                 */
407                public Builder algorithm(final Algorithm alg) {
408
409                        this.alg = alg;
410                        return this;
411                }
412
413                /**
414                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 
415                 * to match a specific key. This can be used, for instance, to 
416                 * choose a key within a {@link JWKSet} during key rollover. 
417                 * The key ID may also correspond to a JWS/JWE {@code kid} 
418                 * header parameter value.
419                 *
420                 * @param kid The key ID, {@code null} if not specified.
421                 *
422                 * @return This builder.
423                 */
424                public Builder keyID(final String kid) {
425
426                        this.kid = kid;
427                        return this;
428                }
429
430
431                /**
432                 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK
433                 * thumbprint (RFC 7638). The key ID can be used to match a
434                 * specific key. This can be used, for instance, to choose a
435                 * key within a {@link JWKSet} during key rollover. The key ID
436                 * may also correspond to a JWS/JWE {@code kid} header
437                 * parameter value.
438                 *
439                 * @return This builder.
440                 *
441                 * @throws JOSEException If the SHA-256 hash algorithm is not
442                 *                       supported.
443                 */
444                public Builder keyIDFromThumbprint()
445                        throws JOSEException {
446
447                        return keyIDFromThumbprint("SHA-256");
448                }
449
450
451                /**
452                 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint
453                 * (RFC 7638). The key ID can be used to match a specific key.
454                 * This can be used, for instance, to choose a key within a
455                 * {@link JWKSet} during key rollover. The key ID may also
456                 * correspond to a JWS/JWE {@code kid} header parameter value.
457                 *
458                 * @param hashAlg The hash algorithm for the JWK thumbprint
459                 *                computation. Must not be {@code null}.
460                 *
461                 * @return This builder.
462                 *
463                 * @throws JOSEException If the hash algorithm is not
464                 *                       supported.
465                 */
466                public Builder keyIDFromThumbprint(final String hashAlg)
467                        throws JOSEException {
468
469                        // Put mandatory params in sorted order
470                        LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
471                        requiredParams.put("crv", crv.toString());
472                        requiredParams.put("kty", KeyType.EC.getValue());
473                        requiredParams.put("x", x.toString());
474                        requiredParams.put("y", y.toString());
475                        this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString();
476                        return this;
477                }
478
479
480                /**
481                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
482                 *
483                 * @param x5u The X.509 certificate URL, {@code null} if not 
484                 *            specified.
485                 *
486                 * @return This builder.
487                 */
488                public Builder x509CertURL(final URI x5u) {
489
490                        this.x5u = x5u;
491                        return this;
492                }
493
494
495                /**
496                 * Sets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of
497                 * the JWK.
498                 *
499                 * @param x5t The X.509 certificate SHA-1 thumbprint,
500                 *            {@code null} if not specified.
501                 *
502                 * @return This builder.
503                 */
504                @Deprecated
505                public Builder x509CertThumbprint(final Base64URL x5t) {
506
507                        this.x5t = x5t;
508                        return this;
509                }
510
511
512                /**
513                 * Sets the X.509 certificate SHA-256 thumbprint
514                 * ({@code x5t#S256}) of the JWK.
515                 *
516                 * @param x5t256 The X.509 certificate SHA-256 thumbprint,
517                 *               {@code null} if not specified.
518                 *
519                 * @return This builder.
520                 */
521                public Builder x509CertSHA256Thumbprint(final Base64URL x5t256) {
522
523                        this.x5t256 = x5t256;
524                        return this;
525                }
526
527
528                /**
529                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
530                 *
531                 * @param x5c The X.509 certificate chain as a unmodifiable 
532                 *            list, {@code null} if not specified.
533                 *
534                 * @return This builder.
535                 */
536                public Builder x509CertChain(final List<Base64> x5c) {
537
538                        this.x5c = x5c;
539                        return this;
540                }
541                
542                
543                /**
544                 * Sets the underlying key store.
545                 *
546                 * @param keyStore Reference to the underlying key store,
547                 *                 {@code null} if none.
548                 *
549                 * @return This builder.
550                 */
551                public Builder keyStore(final KeyStore keyStore) {
552                        
553                        this.ks = keyStore;
554                        return this;
555                }
556
557
558                /**
559                 * Builds a new Elliptic Curve JWK.
560                 *
561                 * @return The Elliptic Curve JWK.
562                 *
563                 * @throws IllegalStateException If the JWK parameters were
564                 *                               inconsistently specified.
565                 */
566                public ECKey build() {
567
568                        try {
569                                if (d == null && priv == null) {
570                                        // Public key
571                                        return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
572                                }
573                                
574                                if (priv != null) {
575                                        // PKCS#11 reference to private key
576                                        return new ECKey(crv, x, y, priv, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
577                                }
578
579                                // Public / private key pair with 'd'
580                                return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
581
582                        } catch (IllegalArgumentException e) {
583                                throw new IllegalStateException(e.getMessage(), e);
584                        }
585                }
586        }
587
588
589        /**
590         * Returns the Base64URL encoding of the specified elliptic curve 'x',
591         * 'y' or 'd' coordinate, with leading zero padding up to the specified
592         * field size in bits.
593         *
594         * @param fieldSize  The field size in bits.
595         * @param coordinate The elliptic curve coordinate. Must not be
596         *                   {@code null}.
597         *
598         * @return The Base64URL-encoded coordinate, with leading zero padding
599         *         up to the curve's field size.
600         */
601        public static Base64URL encodeCoordinate(final int fieldSize, final BigInteger coordinate) {
602
603                final byte[] notPadded = BigIntegerUtils.toBytesUnsigned(coordinate);
604
605                int bytesToOutput = (fieldSize + 7)/8;
606
607                if (notPadded.length >= bytesToOutput) {
608                        // Greater-than check to prevent exception on malformed
609                        // key below
610                        return Base64URL.encode(notPadded);
611                }
612
613                final byte[] padded = new byte[bytesToOutput];
614
615                System.arraycopy(notPadded, 0, padded, bytesToOutput - notPadded.length, notPadded.length);
616
617                return Base64URL.encode(padded);
618        }
619
620
621        /**
622         * The curve name.
623         */
624        private final Curve crv;
625
626
627        /**
628         * The public 'x' EC coordinate.
629         */
630        private final Base64URL x;
631
632
633        /**
634         * The public 'y' EC coordinate.
635         */
636        private final Base64URL y;
637        
638
639        /**
640         * The private 'd' EC coordinate.
641         */
642        private final Base64URL d;
643        
644        
645        /**
646         * Private PKCS#11 key handle.
647         */
648        private final PrivateKey privateKey;
649        
650        
651        /**
652         * Ensures the specified 'x' and 'y' public coordinates are on the
653         * given curve.
654         *
655         * @param crv The curve. Must not be {@code null}.
656         * @param x   The public 'x' coordinate. Must not be {@code null}.
657         * @param y   The public 'y' coordinate. Must not be {@code null}.
658         */
659        private static void ensurePublicCoordinatesOnCurve(final Curve crv, final Base64URL x, final Base64URL y) {
660                
661                if (! SUPPORTED_CURVES.contains(crv)) {
662                        throw new IllegalArgumentException("Unknown / unsupported curve: " + crv);
663                }
664                
665                if (! ECChecks.isPointOnCurve(x.decodeToBigInteger(), y.decodeToBigInteger(), crv.toECParameterSpec())) {
666                        throw new IllegalArgumentException("Invalid EC JWK: The 'x' and 'y' public coordinates are not on the " + crv + " curve");
667                }
668        }
669
670
671        /**
672         * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
673         * specified parameters.
674         *
675         * @param crv    The cryptographic curve. Must not be {@code null}.
676         * @param x      The public 'x' coordinate for the elliptic curve
677         *               point. It is represented as the Base64URL encoding of
678         *               the coordinate's big endian representation. Must not
679         *               be {@code null}.
680         * @param y      The public 'y' coordinate for the elliptic curve
681         *               point. It is represented as the Base64URL encoding of
682         *               the coordinate's big endian representation. Must not
683         *               be {@code null}.
684         * @param use    The key use, {@code null} if not specified or if the
685         *               key is intended for signing as well as encryption.
686         * @param ops    The key operations, {@code null} if not specified.
687         * @param alg    The intended JOSE algorithm for the key, {@code null}
688         *               if not specified.
689         * @param kid    The key ID, {@code null} if not specified.
690         * @param x5u    The X.509 certificate URL, {@code null} if not
691         *               specified.
692         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
693         *               if not specified.
694         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
695         *               if not specified.
696         * @param x5c    The X.509 certificate chain, {@code null} if not
697         *               specified.
698         * @param ks     Reference to the underlying key store, {@code null} if
699         *               not specified.
700         */
701        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 
702                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
703                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
704                     final KeyStore ks) {
705
706                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
707
708                if (crv == null) {
709                        throw new IllegalArgumentException("The curve must not be null");
710                }
711
712                this.crv = crv;
713
714                if (x == null) {
715                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
716                }
717
718                this.x = x;
719
720                if (y == null) {
721                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
722                }
723
724                this.y = y;
725                
726                ensurePublicCoordinatesOnCurve(crv, x, y);
727                
728                ensureMatches(getParsedX509CertChain());
729
730                this.d = null;
731                
732                this.privateKey = null;
733        }
734
735
736        /**
737         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
738         * with the specified parameters.
739         *
740         * @param crv    The cryptographic curve. Must not be {@code null}.
741         * @param x      The public 'x' coordinate for the elliptic curve
742         *               point. It is represented as the Base64URL encoding of
743         *               the coordinate's big endian representation. Must not
744         *               be {@code null}.
745         * @param y      The public 'y' coordinate for the elliptic curve
746         *               point. It is represented as the Base64URL encoding of
747         *               the coordinate's big endian representation. Must not
748         *               be {@code null}.
749         * @param d      The private 'd' coordinate for the elliptic curve
750         *               point. It is represented as the Base64URL encoding of
751         *               the coordinate's big endian representation. Must not
752         *               be {@code null}.
753         * @param use    The key use, {@code null} if not specified or if the
754         *               key is intended for signing as well as encryption.
755         * @param ops    The key operations, {@code null} if not specified.
756         * @param alg    The intended JOSE algorithm for the key, {@code null}
757         *               if not specified.
758         * @param kid    The key ID, {@code null} if not specified.
759         * @param x5u    The X.509 certificate URL, {@code null} if not
760         *               specified.
761         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
762         *               if not specified.
763         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
764         *               if not specified.
765         * @param x5c    The X.509 certificate chain, {@code null} if not
766         *               specified.
767         * @param ks     Reference to the underlying key store, {@code null} if
768         *               not specified.
769         */
770        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final Base64URL d,
771                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
772                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
773                     final KeyStore ks) {
774
775                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
776                
777                if (crv == null) {
778                        throw new IllegalArgumentException("The curve must not be null");
779                }
780
781                this.crv = crv;
782
783                if (x == null) {
784                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
785                }
786
787                this.x = x;
788
789                if (y == null) {
790                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
791                }
792
793                this.y = y;
794                
795                ensurePublicCoordinatesOnCurve(crv, x, y);
796                
797                ensureMatches(getParsedX509CertChain());
798                
799                if (d == null) {
800                        throw new IllegalArgumentException("The 'd' coordinate must not be null");
801                }
802
803                this.d = d;
804                
805                this.privateKey = null;
806        }
807
808
809        /**
810         * Creates a new public / private Elliptic Curve JSON Web Key (JWK)
811         * with the specified parameters. The private key is specified by its
812         * PKCS#11 handle.
813         *
814         * @param crv    The cryptographic curve. Must not be {@code null}.
815         * @param x      The public 'x' coordinate for the elliptic curve
816         *               point. It is represented as the Base64URL encoding of
817         *               the coordinate's big endian representation. Must not
818         *               be {@code null}.
819         * @param y      The public 'y' coordinate for the elliptic curve
820         *               point. It is represented as the Base64URL encoding of
821         *               the coordinate's big endian representation. Must not
822         *               be {@code null}.
823         * @param priv   The private key as a PKCS#11 handle, {@code null} if
824         *               not specified.
825         * @param use    The key use, {@code null} if not specified or if the
826         *               key is intended for signing as well as encryption.
827         * @param ops    The key operations, {@code null} if not specified.
828         * @param alg    The intended JOSE algorithm for the key, {@code null}
829         *               if not specified.
830         * @param kid    The key ID, {@code null} if not specified.
831         * @param x5u    The X.509 certificate URL, {@code null} if not
832         *               specified.
833         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
834         *               if not specified.
835         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
836         *               if not specified.
837         * @param x5c    The X.509 certificate chain, {@code null} if not
838         *               specified.
839         */
840        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final PrivateKey priv,
841                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
842                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
843                     final KeyStore ks) {
844
845                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
846
847                if (crv == null) {
848                        throw new IllegalArgumentException("The curve must not be null");
849                }
850
851                this.crv = crv;
852
853                if (x == null) {
854                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
855                }
856
857                this.x = x;
858
859                if (y == null) {
860                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
861                }
862
863                this.y = y;
864                
865                ensurePublicCoordinatesOnCurve(crv, x, y);
866                
867                ensureMatches(getParsedX509CertChain());
868                
869                d = null;
870                
871                this.privateKey = priv;
872        }
873
874
875        /**
876         * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
877         * specified parameters.
878         *
879         * @param crv    The cryptographic curve. Must not be {@code null}.
880         * @param pub    The public EC key to represent. Must not be
881         *               {@code null}.
882         * @param use    The key use, {@code null} if not specified or if the
883         *               key is intended for signing as well as encryption.
884         * @param ops    The key operations, {@code null} if not specified.
885         * @param alg    The intended JOSE algorithm for the key, {@code null}
886         *               if not specified.
887         * @param kid    The key ID, {@code null} if not specified.
888         * @param x5u    The X.509 certificate URL, {@code null} if not
889         *               specified.
890         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
891         *               if not specified.
892         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
893         *               if not specified.
894         * @param x5c    The X.509 certificate chain, {@code null} if not
895         *               specified.
896         * @param ks     Reference to the underlying key store, {@code null} if
897         *               not specified.
898         */
899        public ECKey(final Curve crv, final ECPublicKey pub, 
900                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
901                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
902                     final KeyStore ks) {
903
904                this(crv, 
905                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
906                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
907                     use, ops, alg, kid,
908                     x5u, x5t, x5t256, x5c,
909                     ks);
910        }
911
912
913        /**
914         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
915         * with the specified parameters.
916         *
917         * @param crv    The cryptographic curve. Must not be {@code null}.
918         * @param pub    The public EC key to represent. Must not be
919         *               {@code null}.
920         * @param priv   The private EC key to represent. Must not be
921         *               {@code null}.
922         * @param use    The key use, {@code null} if not specified or if the
923         *               key is intended for signing as well as encryption.
924         * @param ops    The key operations, {@code null} if not specified.
925         * @param alg    The intended JOSE algorithm for the key, {@code null}
926         *               if not specified.
927         * @param kid    The key ID, {@code null} if not specified.
928         * @param x5u    The X.509 certificate URL, {@code null} if not
929         *               specified.
930         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
931         *               if not specified.
932         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
933         *               if not specified.
934         * @param x5c    The X.509 certificate chain, {@code null} if not
935         *               specified.
936         * @param ks     Reference to the underlying key store, {@code null} if
937         *               not specified.
938         */
939        public ECKey(final Curve crv, final ECPublicKey pub, final ECPrivateKey priv, 
940                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
941                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
942                     final KeyStore ks) {
943
944                this(crv,
945                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
946                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
947                     encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()),
948                     use, ops, alg, kid,
949                     x5u, x5t, x5t256, x5c,
950                     ks);
951        }
952
953
954        /**
955         * Creates a new public / private Elliptic Curve JSON Web Key (JWK)
956         * with the specified parameters. The private key is specified by its
957         * PKCS#11 handle.
958         *
959         * @param crv    The cryptographic curve. Must not be {@code null}.
960         * @param pub    The public EC key to represent. Must not be
961         *               {@code null}.
962         * @param priv   The private key as a PKCS#11 handle, {@code null} if
963         *               not specified.
964         * @param use    The key use, {@code null} if not specified or if the
965         *               key is intended for signing as well as encryption.
966         * @param ops    The key operations, {@code null} if not specified.
967         * @param alg    The intended JOSE algorithm for the key, {@code null}
968         *               if not specified.
969         * @param kid    The key ID, {@code null} if not specified.
970         * @param x5u    The X.509 certificate URL, {@code null} if not
971         *               specified.
972         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
973         *               if not specified.
974         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
975         *               if not specified.
976         * @param x5c    The X.509 certificate chain, {@code null} if not
977         *               specified.
978         * @param ks     Reference to the underlying key store, {@code null} if
979         *               not specified.
980         */
981        public ECKey(final Curve crv, final ECPublicKey pub, final PrivateKey priv,
982                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
983                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
984                     final KeyStore ks) {
985                
986                this(
987                        crv,
988                        encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
989                        encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
990                        priv,
991                        use, ops, alg, kid, x5u, x5t, x5t256, x5c,
992                        ks);
993        }
994
995
996        @Override
997        public Curve getCurve() {
998
999                return crv;
1000        }
1001
1002
1003        /**
1004         * Gets the public 'x' coordinate for the elliptic curve point.
1005         *
1006         * @return The 'x' coordinate. It is represented as the Base64URL 
1007         *         encoding of the coordinate's big endian representation.
1008         */
1009        public Base64URL getX() {
1010
1011                return x;
1012        }
1013
1014
1015        /**
1016         * Gets the public 'y' coordinate for the elliptic curve point.
1017         *
1018         * @return The 'y' coordinate. It is represented as the Base64URL 
1019         *         encoding of the coordinate's big endian representation.
1020         */
1021        public Base64URL getY() {
1022
1023                return y;
1024        }
1025
1026        
1027        /**
1028         * Gets the private 'd' coordinate for the elliptic curve point. It is 
1029         * represented as the Base64URL encoding of the coordinate's big endian 
1030         * representation.
1031         *
1032         * @return The 'd' coordinate.  It is represented as the Base64URL 
1033         *         encoding of the coordinate's big endian representation. 
1034         *         {@code null} if not specified (for a public key).
1035         */
1036        public Base64URL getD() {
1037
1038                return d;
1039        }
1040
1041
1042        /**
1043         * Returns a standard {@code java.security.interfaces.ECPublicKey} 
1044         * representation of this Elliptic Curve JWK. Uses the default JCA
1045         * provider.
1046         * 
1047         * @return The public Elliptic Curve key.
1048         * 
1049         * @throws JOSEException If EC is not supported by the underlying Java
1050         *                       Cryptography (JCA) provider or if the JWK
1051         *                       parameters are invalid for a public EC key.
1052         */
1053        public ECPublicKey toECPublicKey()
1054                throws JOSEException {
1055
1056                return toECPublicKey(null);
1057        }
1058
1059
1060        /**
1061         * Returns a standard {@code java.security.interfaces.ECPublicKey}
1062         * representation of this Elliptic Curve JWK.
1063         *
1064         * @param provider The specific JCA provider to use, {@code null}
1065         *                 implies the default one.
1066         *
1067         * @return The public Elliptic Curve key.
1068         *
1069         * @throws JOSEException If EC is not supported by the underlying Java
1070         *                       Cryptography (JCA) provider or if the JWK
1071         *                       parameters are invalid for a public EC key.
1072         */
1073        public ECPublicKey toECPublicKey(final Provider provider)
1074                throws JOSEException {
1075
1076                ECParameterSpec spec = crv.toECParameterSpec();
1077
1078                if (spec == null) {
1079                        throw new JOSEException("Couldn't get EC parameter spec for curve " + crv);
1080                }
1081
1082                ECPoint w = new ECPoint(x.decodeToBigInteger(), y.decodeToBigInteger());
1083
1084                ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, spec);
1085
1086                try {
1087                        KeyFactory keyFactory;
1088
1089                        if (provider == null) {
1090                                keyFactory = KeyFactory.getInstance("EC");
1091                        } else {
1092                                keyFactory = KeyFactory.getInstance("EC", provider);
1093                        }
1094
1095                        return (ECPublicKey) keyFactory.generatePublic(publicKeySpec);
1096
1097                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
1098
1099                        throw new JOSEException(e.getMessage(), e);
1100                }
1101        }
1102        
1103
1104        /**
1105         * Returns a standard {@code java.security.interfaces.ECPrivateKey} 
1106         * representation of this Elliptic Curve JWK. Uses the default JCA
1107         * provider.
1108         * 
1109         * @return The private Elliptic Curve key, {@code null} if not 
1110         *         specified by this JWK.
1111         * 
1112         * @throws JOSEException If EC is not supported by the underlying Java
1113         *                       Cryptography (JCA) provider or if the JWK
1114         *                       parameters are invalid for a private EC key.
1115         */
1116        public ECPrivateKey toECPrivateKey()
1117                throws JOSEException {
1118
1119                return toECPrivateKey(null);
1120        }
1121
1122
1123        /**
1124         * Returns a standard {@code java.security.interfaces.ECPrivateKey}
1125         * representation of this Elliptic Curve JWK.
1126         *
1127         * @param provider The specific JCA provider to use, {@code null}
1128         *                 implies the default one.
1129         *
1130         * @return The private Elliptic Curve key, {@code null} if not
1131         *         specified by this JWK.
1132         *
1133         * @throws JOSEException If EC is not supported by the underlying Java
1134         *                       Cryptography (JCA) provider or if the JWK
1135         *                       parameters are invalid for a private EC key.
1136         */
1137        public ECPrivateKey toECPrivateKey(final Provider provider)
1138                throws JOSEException {
1139
1140                if (d == null) {
1141                        // No private 'd' param
1142                        return null;
1143                }
1144
1145                ECParameterSpec spec = crv.toECParameterSpec();
1146
1147                if (spec == null) {
1148                        throw new JOSEException("Couldn't get EC parameter spec for curve " + crv);
1149                }
1150
1151                ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d.decodeToBigInteger(), spec);
1152
1153                try {
1154                        KeyFactory keyFactory;
1155
1156                        if (provider == null) {
1157                                keyFactory = KeyFactory.getInstance("EC");
1158                        } else {
1159                                keyFactory = KeyFactory.getInstance("EC", provider);
1160                        }
1161
1162                        return (ECPrivateKey) keyFactory.generatePrivate(privateKeySpec);
1163
1164                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
1165
1166                        throw new JOSEException(e.getMessage(), e);
1167                }
1168        }
1169
1170
1171        @Override
1172        public PublicKey toPublicKey()
1173                throws JOSEException {
1174
1175                return toECPublicKey();
1176        }
1177
1178
1179        @Override
1180        public PrivateKey toPrivateKey()
1181                throws JOSEException {
1182                
1183                PrivateKey prv = toECPrivateKey();
1184                
1185                if (prv != null) {
1186                        // Return private EC key with key material
1187                        return prv;
1188                }
1189                
1190                // Return private EC key as PKCS#11 handle, or null
1191                return privateKey;
1192        }
1193        
1194
1195        /**
1196         * Returns a standard {@code java.security.KeyPair} representation of 
1197         * this Elliptic Curve JWK. Uses the default JCA provider.
1198         * 
1199         * @return The Elliptic Curve key pair. The private Elliptic Curve key 
1200         *         will be {@code null} if not specified.
1201         * 
1202         * @throws JOSEException If EC is not supported by the underlying Java
1203         *                       Cryptography (JCA) provider or if the JWK
1204         *                       parameters are invalid for a public and / or
1205         *                       private EC key.
1206         */
1207        @Override
1208        public KeyPair toKeyPair()
1209                throws JOSEException {
1210
1211                return toKeyPair(null);
1212        }
1213
1214
1215        /**
1216         * Returns a standard {@code java.security.KeyPair} representation of
1217         * this Elliptic Curve JWK.
1218         *
1219         * @param provider The specific JCA provider to use, {@code null}
1220         *                 implies the default one.
1221         *
1222         * @return The Elliptic Curve key pair. The private Elliptic Curve key
1223         *         will be {@code null} if not specified.
1224         *
1225         * @throws JOSEException If EC is not supported by the underlying Java
1226         *                       Cryptography (JCA) provider or if the JWK
1227         *                       parameters are invalid for a public and / or
1228         *                       private EC key.
1229         */
1230        public KeyPair toKeyPair(final Provider provider)
1231                throws JOSEException {
1232
1233                if (privateKey != null) {
1234                        // Private key as PKCS#11 handle
1235                        return new KeyPair(toECPublicKey(provider), privateKey);
1236                } else {
1237                        return new KeyPair(toECPublicKey(provider), toECPrivateKey(provider));
1238                }
1239        }
1240        
1241        
1242        @Override
1243        public boolean matches(final X509Certificate cert) {
1244                
1245                ECPublicKey certECKey;
1246                try {
1247                        certECKey = (ECPublicKey) getParsedX509CertChain().get(0).getPublicKey();
1248                } catch (ClassCastException ex) {
1249                        return false;
1250                }
1251                // Compare Big Ints, base64url encoding may have padding!
1252                // https://tools.ietf.org/html/rfc7518#section-6.2.1.2
1253                if (! getX().decodeToBigInteger().equals(certECKey.getW().getAffineX())) {
1254                        return false;
1255                }
1256                if (! getY().decodeToBigInteger().equals(certECKey.getW().getAffineY())) {
1257                        return false;
1258                }
1259                return true;
1260        }
1261        
1262        
1263        /**
1264         * Calls {@link #matches(X509Certificate)} for the first X.509
1265         * certificate in the specified chain.
1266         *
1267         * @param chain The X.509 certificate chain, {@code null} if not
1268         *              specified.
1269         *
1270         * @throws IllegalArgumentException If a certificate chain is specified
1271         *                                  and the first certificate in it
1272         *                                  doesn't match.
1273         */
1274        private void ensureMatches(final List<X509Certificate> chain) {
1275                
1276                if (chain == null)
1277                        return;
1278                
1279                if (! matches(chain.get(0)))
1280                        throw new IllegalArgumentException("The public subject key info of the first X.509 certificate in the chain must match the JWK type and public parameters");
1281        }
1282        
1283        
1284        @Override
1285        public LinkedHashMap<String,?> getRequiredParams() {
1286
1287                // Put mandatory params in sorted order
1288                LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
1289                requiredParams.put("crv", crv.toString());
1290                requiredParams.put("kty", getKeyType().getValue());
1291                requiredParams.put("x", x.toString());
1292                requiredParams.put("y", y.toString());
1293                return requiredParams;
1294        }
1295
1296
1297        @Override
1298        public boolean isPrivate() {
1299
1300                return d != null || privateKey != null;
1301        }
1302
1303
1304        @Override
1305        public int size() {
1306
1307                ECParameterSpec ecParameterSpec = crv.toECParameterSpec();
1308
1309                if (ecParameterSpec == null) {
1310                        throw new UnsupportedOperationException("Couldn't determine field size for curve " + crv.getName());
1311                }
1312
1313                return ecParameterSpec.getCurve().getField().getFieldSize();
1314        }
1315
1316        
1317        /**
1318         * Returns a copy of this Elliptic Curve JWK with any private values 
1319         * removed.
1320         *
1321         * @return The copied public Elliptic Curve JWK.
1322         */
1323        @Override
1324        public ECKey toPublicJWK() {
1325
1326                return new ECKey(
1327                        getCurve(), getX(), getY(),
1328                        getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(),
1329                        getX509CertURL(), getX509CertThumbprint(), getX509CertSHA256Thumbprint(), getX509CertChain(),
1330                        getKeyStore());
1331        }
1332        
1333
1334        @Override
1335        public JSONObject toJSONObject() {
1336
1337                JSONObject o = super.toJSONObject();
1338
1339                // Append EC specific attributes
1340                o.put("crv", crv.toString());
1341                o.put("x", x.toString());
1342                o.put("y", y.toString());
1343
1344                if (d != null) {
1345                        o.put("d", d.toString());
1346                }
1347                
1348                return o;
1349        }
1350
1351
1352        /**
1353         * Parses a public / private Elliptic Curve JWK from the specified JSON
1354         * object string representation.
1355         *
1356         * @param s The JSON object string to parse. Must not be {@code null}.
1357         *
1358         * @return The public / private Elliptic Curve JWK.
1359         *
1360         * @throws ParseException If the string couldn't be parsed to an
1361         *                        Elliptic Curve JWK.
1362         */
1363        public static ECKey parse(final String s)
1364                throws ParseException {
1365
1366                return parse(JSONObjectUtils.parse(s));
1367        }
1368
1369
1370        /**
1371         * Parses a public / private Elliptic Curve JWK from the specified JSON
1372         * object representation.
1373         *
1374         * @param jsonObject The JSON object to parse. Must not be 
1375         *                   {@code null}.
1376         *
1377         * @return The public / private Elliptic Curve JWK.
1378         *
1379         * @throws ParseException If the JSON object couldn't be parsed to an 
1380         *                        Elliptic Curve JWK.
1381         */
1382        public static ECKey parse(final JSONObject jsonObject)
1383                throws ParseException {
1384
1385                // Parse the mandatory parameters first
1386                Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv"));
1387                Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x"));
1388                Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y"));
1389
1390                // Check key type
1391                KeyType kty = JWKMetadata.parseKeyType(jsonObject);
1392
1393                if (kty != KeyType.EC) {
1394                        throw new ParseException("The key type \"kty\" must be EC", 0);
1395                }
1396
1397                // Get optional private key
1398                Base64URL d = null;
1399                if (jsonObject.get("d") != null) {
1400                        d = new Base64URL(JSONObjectUtils.getString(jsonObject, "d"));
1401                }
1402
1403
1404                try {
1405                        if (d == null) {
1406                                // Public key
1407                                return new ECKey(crv, x, y,
1408                                        JWKMetadata.parseKeyUse(jsonObject),
1409                                        JWKMetadata.parseKeyOperations(jsonObject),
1410                                        JWKMetadata.parseAlgorithm(jsonObject),
1411                                        JWKMetadata.parseKeyID(jsonObject),
1412                                        JWKMetadata.parseX509CertURL(jsonObject),
1413                                        JWKMetadata.parseX509CertThumbprint(jsonObject),
1414                                        JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject),
1415                                        JWKMetadata.parseX509CertChain(jsonObject),
1416                                        null);
1417
1418                        } else {
1419                                // Key pair
1420                                return new ECKey(crv, x, y, d,
1421                                        JWKMetadata.parseKeyUse(jsonObject),
1422                                        JWKMetadata.parseKeyOperations(jsonObject),
1423                                        JWKMetadata.parseAlgorithm(jsonObject),
1424                                        JWKMetadata.parseKeyID(jsonObject),
1425                                        JWKMetadata.parseX509CertURL(jsonObject),
1426                                        JWKMetadata.parseX509CertThumbprint(jsonObject),
1427                                        JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject),
1428                                        JWKMetadata.parseX509CertChain(jsonObject),
1429                                        null);
1430                        }
1431
1432                } catch (IllegalArgumentException ex) {
1433
1434                        // Conflicting 'use' and 'key_ops'
1435                        throw new ParseException(ex.getMessage(), 0);
1436                }
1437        }
1438        
1439        
1440        /**
1441         * Parses a public Elliptic Curve JWK from the specified X.509
1442         * certificate. Requires BouncyCastle.
1443         *
1444         * <p><strong>Important:</strong> The X.509 certificate is not
1445         * validated!
1446         *
1447         * <p>Sets the following JWK parameters:
1448         *
1449         * <ul>
1450         *     <li>The curve is obtained from the subject public key info
1451         *         algorithm parameters.
1452         *     <li>The JWK use inferred by {@link KeyUse#from}.
1453         *     <li>The JWK ID from the X.509 serial number (in base 10).
1454         *     <li>The JWK X.509 certificate chain (this certificate only).
1455         *     <li>The JWK X.509 certificate SHA-256 thumbprint.
1456         * </ul>
1457         *
1458         * @param cert The X.509 certificate. Must not be {@code null}.
1459         *
1460         * @return The public Elliptic Curve JWK.
1461         *
1462         * @throws JOSEException If parsing failed.
1463         */
1464        public static ECKey parse(final X509Certificate cert)
1465                throws JOSEException {
1466                
1467                if (! (cert.getPublicKey() instanceof ECPublicKey)) {
1468                        throw new JOSEException("The public key of the X.509 certificate is not EC");
1469                }
1470                
1471                ECPublicKey publicKey = (ECPublicKey) cert.getPublicKey();
1472                
1473                try {
1474                        JcaX509CertificateHolder certHolder = new JcaX509CertificateHolder(cert);
1475                        
1476                        String oid = certHolder.getSubjectPublicKeyInfo().getAlgorithm().getParameters().toString();
1477                        
1478                        Curve crv = Curve.forOID(oid);
1479                        
1480                        if (crv == null) {
1481                                throw new JOSEException("Couldn't determine EC JWK curve for OID " + oid);
1482                        }
1483                        
1484                        MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
1485                        
1486                        return new ECKey.Builder(crv, publicKey)
1487                                .keyUse(KeyUse.from(cert))
1488                                .keyID(cert.getSerialNumber().toString(10))
1489                                .x509CertChain(Collections.singletonList(Base64.encode(cert.getEncoded())))
1490                                .x509CertSHA256Thumbprint(Base64URL.encode(sha256.digest(cert.getEncoded())))
1491                                .build();
1492                } catch (NoSuchAlgorithmException e) {
1493                        throw new JOSEException("Couldn't encode x5t parameter: " + e.getMessage(), e);
1494                } catch (CertificateEncodingException e) {
1495                        throw new JOSEException("Couldn't encode x5c parameter: " + e.getMessage(), e);
1496                }
1497        }
1498        
1499        
1500        /**
1501         * Loads a public / private Elliptic Curve JWK from the specified JCA
1502         * key store. Requires BouncyCastle.
1503         *
1504         * <p><strong>Important:</strong> The X.509 certificate is not
1505         * validated!
1506         *
1507         * @param keyStore The key store. Must not be {@code null}.
1508         * @param alias    The alias. Must not be {@code null}.
1509         * @param pin      The pin to unlock the private key if any, empty or
1510         *                 {@code null} if not required.
1511         *
1512         * @return The public / private Elliptic Curve JWK., {@code null} if no
1513         *         key with the specified alias was found.
1514         *
1515         * @throws KeyStoreException On a key store exception.
1516         * @throws JOSEException     If EC key loading failed.
1517         */
1518        public static ECKey load(final KeyStore keyStore,
1519                                 final String alias,
1520                                 final char[] pin)
1521                throws KeyStoreException, JOSEException {
1522                
1523                Certificate cert = keyStore.getCertificate(alias);
1524                
1525                if (cert == null || ! (cert instanceof X509Certificate)) {
1526                        return null;
1527                }
1528                
1529                X509Certificate x509Cert = (X509Certificate)cert;
1530                
1531                if (! (x509Cert.getPublicKey() instanceof ECPublicKey)) {
1532                        throw new JOSEException("Couldn't load EC JWK: The key algorithm is not EC");
1533                }
1534                        
1535                ECKey ecJWK = ECKey.parse(x509Cert);
1536                
1537                // Let kid=alias
1538                ecJWK = new ECKey.Builder(ecJWK).keyID(alias).keyStore(keyStore).build();
1539                
1540                // Check for private counterpart
1541                Key key;
1542                try {
1543                        key = keyStore.getKey(alias, pin);
1544                } catch (UnrecoverableKeyException | NoSuchAlgorithmException e) {
1545                        throw new JOSEException("Couldn't retrieve private EC key (bad pin?): " + e.getMessage(), e);
1546                }
1547                        
1548                if (key instanceof ECPrivateKey) {
1549                        // Simple file based key store
1550                        return new ECKey.Builder(ecJWK)
1551                                .privateKey((ECPrivateKey)key)
1552                                .build();
1553                } else if (key instanceof PrivateKey && "EC".equalsIgnoreCase(key.getAlgorithm())) {
1554                        // PKCS#11 store
1555                        return new ECKey.Builder(ecJWK)
1556                                .privateKey((PrivateKey)key)
1557                                .build();
1558                } else {
1559                        return ecJWK;
1560                }
1561        }
1562
1563        
1564        @Override
1565        public boolean equals(Object o) {
1566                if (this == o) return true;
1567                if (!(o instanceof ECKey)) return false;
1568                if (!super.equals(o)) return false;
1569                ECKey ecKey = (ECKey) o;
1570                return Objects.equals(crv, ecKey.crv) &&
1571                                Objects.equals(x, ecKey.x) &&
1572                                Objects.equals(y, ecKey.y) &&
1573                                Objects.equals(d, ecKey.d) &&
1574                                Objects.equals(privateKey, ecKey.privateKey);
1575        }
1576
1577        
1578        @Override
1579        public int hashCode() {
1580                return Objects.hash(super.hashCode(), crv, x, y, d, privateKey);
1581        }
1582}