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