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