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