View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.jose.jws;
18  
19  import java.math.BigInteger;
20  import java.security.interfaces.ECKey;
21  import java.security.spec.ECFieldFp;
22  import java.security.spec.ECParameterSpec;
23  import java.security.spec.ECPoint;
24  import java.security.spec.EllipticCurve;
25  import java.util.Objects;
26  import javax.xml.bind.DatatypeConverter;
27  
28  /**
29   * Enumerates all supported elliptic curve parameters for ESXXX signature formats.
30   */
31  public enum SupportedEllipticCurve {
32      /** NIST P-256. */
33      P256("P-256", StandardCurve.P_256, 64, JwsAlgorithm.ES256),
34      /** NIST P-384. */
35      P384("P-384", StandardCurve.P_384, 96, JwsAlgorithm.ES384),
36      /** NIST P-521. Please note that this is not a typo: ES512 uses curve <em>P-521</em>, which produces a 132-octet
37       * signature value. */
38      P521("P-521", StandardCurve.P_521, 132, JwsAlgorithm.ES512);
39  
40  
41      private final ECParameterSpec parameters;
42      private final String standardName;
43      private final int signatureSize;
44      private final JwsAlgorithm jwsAlgorithm;
45  
46      SupportedEllipticCurve(String standardName, ECParameterSpec curve, int signatureSize, JwsAlgorithm jwsAlgorithm) {
47          this.parameters = curve;
48          this.standardName = standardName;
49          this.signatureSize = signatureSize;
50          this.jwsAlgorithm = jwsAlgorithm;
51      }
52  
53      /**
54       * Returns the parameters for the given elliptic curve.
55       *
56       * @return the elliptic curve algorithm parameters.
57       */
58      public ECParameterSpec getParameters() {
59          return parameters;
60      }
61  
62      /**
63       * Return the name of the curve as used for the "crv" claim in a JWK.
64       *
65       * @return the standard JWA name for the curve.
66       */
67      public String getStandardName() {
68          return standardName;
69      }
70  
71      /**
72       * Returns the size of the signature produced by this curve in octets.
73       *
74       * @return the number of octets (bytes) required to hold a signature of this curve.
75       */
76      public int getSignatureSize() {
77          return signatureSize;
78      }
79  
80      /**
81       * Returns the JwsAlgorithm that corresponds to this elliptic curve.
82       *
83       * @return the corresponding JwsAlgorithm.
84       */
85      public JwsAlgorithm getJwsAlgorithm() {
86          return jwsAlgorithm;
87      }
88  
89      /**
90       * Returns the curve parameters for the given standard curve name (crv claim in a JWK).
91       *
92       * @param curveName the curve name.
93       * @return the curve parameters for the name.
94       * @throws IllegalArgumentException if the curve name is not supported.
95       */
96      public static SupportedEllipticCurve forName(final String curveName) {
97          for (SupportedEllipticCurve candidate : values()) {
98              if (candidate.getStandardName().equals(curveName)) {
99                  return candidate;
100             }
101         }
102         throw new IllegalArgumentException("Unsupported curve: " + curveName);
103     }
104 
105     /**
106      * Determines the standard curve that matches the given (private or public) key. This is done by comparing the
107      * key parameters for an <em>exact</em> match against one of the standard curves. All parameters much match for a
108      * match to succeed.
109      *
110      * @param key the private or public key to determine the curve for.
111      * @return the matching supported curve parameters.
112      * @throws IllegalArgumentException if the key does not match any supported curve parameters.
113      */
114     public static SupportedEllipticCurve forKey(final ECKey key) {
115         final ECParameterSpec params = key.getParams();
116         for (SupportedEllipticCurve supported : values()) {
117             final ECParameterSpec candidateParams = supported.getParameters();
118             if (candidateParams.getCofactor() == params.getCofactor()
119                     && Objects.equals(candidateParams.getCurve(), params.getCurve())
120                     && Objects.equals(candidateParams.getGenerator(), params.getGenerator())
121                     && Objects.equals(candidateParams.getOrder(), params.getOrder())) {
122                 return supported;
123             }
124         }
125         throw new IllegalArgumentException("Unsupported ECKey parameters");
126     }
127 
128     /**
129      * Determines the supported curve parameters for the given signature. This is done purely based on the length of
130      * the signature and the behaviour is not specified if multiple curves could have produced this signature.
131      *
132      * @param signature the signature to match.
133      * @return the curve that produced this signature.
134      * @throws IllegalArgumentException if the signature does not match any supported curve parameters.
135      */
136     public static SupportedEllipticCurve forSignature(byte[] signature) {
137         for (SupportedEllipticCurve candidate : values()) {
138             if (signature.length == candidate.getSignatureSize()) {
139                 return candidate;
140             }
141         }
142         throw new IllegalArgumentException("Unsupported signature size: " + signature.length);
143     }
144 
145     /**
146      * NIST standard elliptic curve parameters as specified in the JSON Web Algorithms (JWA) spec and defined in
147      * <a href="http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf">FIPS 186-4</a> section D.1.2.3 (P-256),
148      * D.1.2.4 (P-384) and D.1.2.5 (P-521). Defined as a separate inner class to avoid illegal forward-reference
149      * problems when constructing the elements of the SupportedEllipticCurve enum.
150      *
151      * <p>
152      * ECDSA uses an elliptic curve defined by all the points from a finite field that satisfy the equation:
153      * <em>y</em><sup>2</sup> = <em>x</em><sup>3</sup> + <em>ax</em> + <em>b</em> where <em>a</em> and <em>b</em> are
154      * the coefficients. For the curves we are interested in for JWA, the finite fields are produced by the integers
155      * modulo some large prime <em>p</em>, so the arithmetic for the above equation is all done modulo <em>p</em>.
156      * In addition, we define a base point on the curve, known as the <em>generator</em> and denoted <em>G</em> (with
157      * components <em>G<sub>x</sub></em> and <em>G<sub>y</sub></em>), such that the <em>order</em> (number of
158      * elements) of the resulting curve is a large prime, <em>n</em>. The number of points on the curve is actually
159      * given by <em>hn</em> where <em>h</em> is the cofactor, but h is fixed to be 1 for all NIST curves so we
160      * ignore it here.
161      *
162      * <p>
163      * The names of the curves (e.g. P-256) are given by the length of the prime modulus, <em>p</em>, in bits. So for
164      * P-256 the prime is 256 bits long when written in binary, etc.
165      *
166      * <p>
167      * The Java {@link ECParameterSpec} expects parameters in a slightly different format from how they are defined
168      * in the NIST specification:
169      * <table>
170      *     <thead>
171      *         <tr><th>NIST Parameter</th><th>Java Parameter</th><th>Description</th></tr>
172      *     </thead>
173      *     <tbody>
174      *         <tr><td><em>p</em></td><td><code>p</code></td><td>The prime modulus</td></tr>
175      *         <tr><td><em>n</em></td><td><code>n</code></td><td>The order of the field</td></tr>
176      *         <tr><td><em>SEED</em></td><td><code>seed</code></td><td>Seed value to SHA-1 used to generate
177      *         the coefficients of the curve as per the algorithm in <a
178      *         href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf">ANSI
179      *         X9.62</a> Annex A.3.3. This can be used to verify that the coefficients have been generated
180      *         pseudo-randomly via the algorithm in A.3.4.2.</td>
181      *         </tr>
182      *         <tr><td><em>c</em></td><td>n/a</td><td>The output of the SHA-1 curve generation algorithm (this is
183      *         called W in the ANSI X9.62 algorithm linked above). It should hold that <em>c</em> *
184      *         <em>b</em><sup>2</sup> = <em>a</em><sup>3</sup> (mod p).</td></tr>
185      *         <tr><td>n/a</td><td><code>a</code></td><td>The first coefficient of the curve equation. For all the NIST
186      *         standard prime curves this is fixed as -3 (mod <em>p</em>).</td>/tr>
187      *         <tr><td><em>b</em></td><td><code>b</code></td><td>The second coefficient of the curve equation.</td></tr>
188      *         <tr><td><em>G<sub>x</sub></em></td><td><code>x</code></td><td>The x-coordinate of the generator point G.
189      *         </td></tr>
190      *         <tr><td><em>G<sub>y</sub></em></td><td><code>y</code></td><td>The y-coordinate of the generator point G.
191      *         </td></tr>
192      *     </tbody>
193      * </table>
194      * <p>
195      * Note that the <em>seed</em> and <em>c</em> values are not required after the coefficients <em>a</em> and
196      * <em>b</em> have been generated. They can be used to verify that the coefficients were pseudo-randomly
197      * generated and not picked by hand (which might indicate a backdoor). We include the seed value for completeness
198      * of the algorithm parameters (ECParameterSpec does not have the ability to specify <em>c</em>, but it can be
199      * derived from the seed and the coefficients).
200      */
201     private static class StandardCurve {
202         private static final int H = 1;
203 
204         /**
205          * The P-256 curve.
206          */
207         private static final ECParameterSpec P_256 = new ECParameterSpec(
208                 new EllipticCurve(
209                         p("115792089210356248762697446949407573530086143415290314195533631308867097853951"),
210                         a("115792089210356248762697446949407573530086143415290314195533631308867097853948"),
211                         b("41058363725152142129326129780047268409114441015993725554835256314039467401291"),
212                         seed("c49d3608 86e70493 6a6678e1 139d26b7 819f7e90")),
213                 new ECPoint(x("48439561293906451759052585252797914202762949526041747995844080717082404635286"),
214                             y("36134250956749795798585127919587881956611106672985015071877198253568414405109")),
215                 n("115792089210356248762697446949407573529996955224135760342422259061068512044369"), H);
216 
217         /**
218          * The P-384 curve.
219          */
220         private static final ECParameterSpec P_384 = new ECParameterSpec(
221                 new EllipticCurve(
222                         p("3940200619639447921227904010014361380507973927046544666794829340424572177149687032904726"
223                                 + "6088258938001861606973112319"),
224                         a("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088"
225                                 + "258938001861606973112316"),
226                         b("27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764"
227                                 + "883745107765439761230575"),
228                         seed("a335926a a319a27a 1d00896a 6773a482 7acdac73")),
229                 new ECPoint(
230                         x("26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053"
231                                 + "975719786650872476732087"),
232                         y("83257109614890299855467512895201081792878530488613155947092059024805031998844192244386437603"
233                                 + "92947333078086511627871")),
234                 n("3940200619639447921227904010014361380507973927046544666794690527962765939911326356939895630815229491"
235                         + "3554433653942643"), H);
236 
237         /**
238          * The P-521 curve.
239          */
240         private static final ECParameterSpec P_521 = new ECParameterSpec(
241                 new EllipticCurve(
242                         p("68647976601306097149819007990813932172694353001433054093944634591855431833976560521225596406"
243                                 + "61454554977296311391480858037121987999716643812574028291115057151"),
244                         a("68647976601306097149819007990813932172694353001433054093944634591855431833976560521225596406"
245                                 + "61454554977296311391480858037121987999716643812574028291115057148"),
246                         b("10938490380737342745111123907668055699362075989516837489945863944959531161507350160137087375"
247                                 + "73759623248592132296706313309438452531591012912142327488478985984"),
248                         seed("d09e8800 291cb853 96cc6717 393284aa a0da64ba")),
249                 new ECPoint(
250                         x("26617408020502170632287687167233609607298591687569731477066713684188029449964278084915450806"
251                                 + "27771902352094241225065558662157113545570916814161637315895999846"),
252                         y("37571800257700204635455072244911836035944551347697624866945677796155444774405563166912344050"
253                                 + "12945539562144444537289428522585666729196580810124344277578376784")),
254                 n("6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197"
255                         + "532963996371363321113864768612440380340372808892707005449"), H);
256 
257 
258         private static ECFieldFp p(final String value) {
259             return new ECFieldFp(new BigInteger(value));
260         }
261 
262         private static BigInteger a(final String value) {
263             return new BigInteger(value);
264         }
265 
266         private static BigInteger b(final String value) {
267             return new BigInteger(value);
268         }
269 
270         private static BigInteger x(final String value) {
271             return new BigInteger(value);
272         }
273 
274         private static BigInteger y(final String value) {
275             return new BigInteger(value);
276         }
277 
278         private static BigInteger n(final String value) {
279             return new BigInteger(value);
280         }
281 
282         private static byte[] seed(final String hex) {
283             return DatatypeConverter.parseHexBinary(hex.replaceAll("\\s+", ""));
284         }
285     }
286 }