001 package com.nimbusds.jose; 002 003 004 import java.text.ParseException; 005 006 import net.jcip.annotations.Immutable; 007 import net.minidev.json.JSONObject; 008 009 import com.nimbusds.jose.util.Base64URL; 010 import com.nimbusds.jose.util.JSONObjectUtils; 011 012 013 /** 014 * Public {@link KeyType#EC Elliptic Curve} JSON Web Key (JWK). This class is 015 * immutable. 016 * 017 * <p>Example JSON: 018 * 019 * <pre> 020 * { 021 * "kty" : "EC", 022 * "crv" : "P-256", 023 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 024 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 025 * "use" : "enc", 026 * "kid" : "1" 027 * } 028 * </pre> 029 * 030 * <p>See http://en.wikipedia.org/wiki/Elliptic_curve_cryptography 031 * 032 * @author Vladimir Dzhuvinov 033 * @version $version$ (2013-01-15) 034 */ 035 @Immutable 036 public final class ECKey extends JWK { 037 038 039 /** 040 * Cryptographic curve. This class is immutable. 041 * 042 * <p>Includes constants for the following standard cryptographic 043 * curves: 044 * 045 * <ul> 046 * <li>{@link #P_256} 047 * <li>{@link #P_384} 048 * <li>{@link #P_521} 049 * </ul> 050 * 051 * <p>See "Digital Signature Standard (DSS)", FIPS PUB 186-3, June 2009, 052 * National Institute of Standards and Technology (NIST). 053 */ 054 @Immutable 055 public static class Curve { 056 057 058 /** 059 * P-256 curve. 060 */ 061 public static final Curve P_256 = new Curve("P-256"); 062 063 064 /** 065 * P-384 curve. 066 */ 067 public static final Curve P_384 = new Curve("P-384"); 068 069 070 /** 071 * P-521 curve. 072 */ 073 public static final Curve P_521 = new Curve("P-521"); 074 075 076 /** 077 * The curve name. 078 */ 079 private final String name; 080 081 082 /** 083 * Creates a new cryptographic curve with the specified name. 084 * 085 * @param name The name of the cryptographic curve. Must not be 086 * {@code null}. 087 */ 088 public Curve(final String name) { 089 090 if (name == null) { 091 throw new IllegalArgumentException("The cryptographic curve name must not be null"); 092 } 093 094 this.name = name; 095 } 096 097 098 /** 099 * Gets the name of this cryptographic curve. 100 * 101 * @return The name. 102 */ 103 public String getName() { 104 105 return name; 106 } 107 108 109 /** 110 * @see #getName 111 */ 112 @Override 113 public String toString() { 114 115 return getName(); 116 } 117 118 119 /** 120 * Overrides {@code Object.equals()}. 121 * 122 * @param object The object to compare to. 123 * 124 * @return {@code true} if the objects have the same value, 125 * otherwise {@code false}. 126 */ 127 @Override 128 public boolean equals(final Object object) { 129 130 return object != null && 131 object instanceof Curve && 132 this.toString().equals(object.toString()); 133 } 134 135 136 /** 137 * Parses a cryptographic curve from the specified string. 138 * 139 * @param s The string to parse. Must not be {@code null}. 140 * 141 * @return The cryptographic curve. 142 * 143 * @throws ParseException If the string couldn't be parsed. 144 */ 145 public static Curve parse(final String s) 146 throws ParseException { 147 148 if (s == null) { 149 throw new IllegalArgumentException("The cryptographic curve sting must not be null"); 150 } 151 152 if (s == P_256.getName()) { 153 return P_256; 154 } else if (s == P_384.getName()) { 155 return P_384; 156 } else if (s == P_521.getName()) { 157 return P_521; 158 } else { 159 return new Curve(s); 160 } 161 } 162 } 163 164 165 /** 166 * The curve name. 167 */ 168 private final Curve crv; 169 170 171 /** 172 * The 'x' EC coordinate. 173 */ 174 private final Base64URL x; 175 176 177 /** 178 * The 'y' EC coordinate. 179 */ 180 private final Base64URL y; 181 182 183 /** 184 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 185 * specified parameters. 186 * 187 * @param crv The cryptographic curve. Must not be {@code null}. 188 * @param x The 'x' coordinate for the elliptic curve point. It is 189 * represented as the Base64URL encoding of the coordinate's 190 * big endian representation. Must not be {@code null}. 191 * @param y The 'y' coordinate for the elliptic curve point. It is 192 * represented as the Base64URL encoding of the coordinate's 193 * big endian representation. Must not be {@code null}. 194 * @param use The key use, {@code null} if not specified. 195 * @param alg The intended JOSE algorithm for the key, {@code null} if 196 * not specified. 197 * @param kid The key ID, {@code null} if not specified. 198 */ 199 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 200 final Use use, final Algorithm alg, final String kid) { 201 202 super(KeyType.EC, use, alg, kid); 203 204 if (crv == null) { 205 throw new IllegalArgumentException("The curve must not be null"); 206 } 207 208 this.crv = crv; 209 210 if (x == null) { 211 throw new IllegalArgumentException("The x coordinate must not be null"); 212 } 213 214 this.x = x; 215 216 if (y == null) { 217 throw new IllegalArgumentException("The y coordinate must not be null"); 218 } 219 220 this.y = y; 221 } 222 223 224 /** 225 * Gets the cryptographic curve. 226 * 227 * @return The cryptographic curve. 228 */ 229 public Curve getCurve() { 230 231 return crv; 232 } 233 234 235 /** 236 * Gets the 'x' coordinate for the elliptic curve point. It is 237 * represented as the Base64URL encoding of the coordinate's big endian 238 * representation. 239 * 240 * @return The 'x' coordinate. 241 */ 242 public Base64URL getX() { 243 244 return x; 245 } 246 247 248 /** 249 * Gets the 'y' coordinate for the elliptic curve point. It is 250 * represented as the Base64URL encoding of the coordinate's big endian 251 * representation. 252 * 253 * @return The 'y' coordinate. 254 */ 255 public Base64URL getY() { 256 257 return y; 258 } 259 260 261 @Override 262 public JSONObject toJSONObject() { 263 264 JSONObject o = super.toJSONObject(); 265 266 // Append EC specific attributes 267 o.put("crv", crv.toString()); 268 o.put("x", x.toString()); 269 o.put("y", y.toString()); 270 271 return o; 272 } 273 274 275 /** 276 * Parses an Elliptic Curve JWK from the specified JSON object 277 * representation. 278 * 279 * @param jsonObject The JSON object to parse. Must not be 280 * {@code null}. 281 * 282 * @return The Elliptic Curve JWK. 283 * 284 * @throws ParseException If the JSON object couldn't be parsed to a 285 * valid Elliptic Curve JWK. 286 */ 287 public static ECKey parse(final JSONObject jsonObject) 288 throws ParseException { 289 290 // Parse the mandatory parameters first 291 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 292 Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv")); 293 Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x")); 294 Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y")); 295 296 // Get optional key use 297 Use use = JWK.parseKeyUse(jsonObject); 298 299 // Get optional intended algorithm 300 Algorithm alg = JWK.parseAlgorithm(jsonObject); 301 302 // Get optional key ID 303 String id = JWK.parseKeyID(jsonObject); 304 305 // Check key type 306 if (kty != KeyType.EC) { 307 throw new ParseException("The key type \"kty\" must be EC", 0); 308 } 309 310 return new ECKey(crv, x, y, use, alg, id); 311 } 312 }