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-08) 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 instanceof Curve && this.toString().equals(object.toString()); 131 } 132 133 134 /** 135 * Parses a cryptographic curve from the specified string. 136 * 137 * @param s The string to parse. Must not be {@code null}. 138 * 139 * @return The cryptographic curve. 140 * 141 * @throws ParseException If the string couldn't be parsed. 142 */ 143 public static Curve parse(final String s) 144 throws ParseException { 145 146 if (s == null) 147 throw new IllegalArgumentException("The cryptographic curve sting must not be null"); 148 149 if (s == P_256.getName()) 150 return P_256; 151 152 else if (s == P_384.getName()) 153 return P_384; 154 155 else if (s == P_521.getName()) 156 return P_521; 157 158 else 159 return new Curve(s); 160 } 161 } 162 163 164 /** 165 * The curve name. 166 */ 167 private final Curve crv; 168 169 170 /** 171 * The 'x' EC coordinate. 172 */ 173 private final Base64URL x; 174 175 176 /** 177 * The 'y' EC coordinate. 178 */ 179 private final Base64URL y; 180 181 182 /** 183 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 184 * specified parameters. 185 * 186 * @param crv The cryptographic curve. Must not be {@code null}. 187 * @param x The 'x' coordinate for the elliptic curve point. It is 188 * represented as the Base64URL encoding of the coordinate's 189 * big endian representation. Must not be {@code null}. 190 * @param y The 'y' coordinate for the elliptic curve point. It is 191 * represented as the Base64URL encoding of the coordinate's 192 * big endian representation. Must not be {@code null}. 193 * @param use The key use, {@code null} if not specified. 194 * @param alg The intended JOSE algorithm for the key, {@code null} if 195 * not specified. 196 * @param kid The key ID, {@code null} if not specified. 197 */ 198 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 199 final Use use, final Algorithm alg, final String kid) { 200 201 super(KeyType.EC, use, alg, kid); 202 203 if (crv == null) 204 throw new IllegalArgumentException("The curve must not be null"); 205 206 this.crv = crv; 207 208 if (x == null) 209 throw new IllegalArgumentException("The x coordinate must not be null"); 210 211 this.x = x; 212 213 if (y == null) 214 throw new IllegalArgumentException("The y coordinate must not be null"); 215 216 this.y = y; 217 } 218 219 220 /** 221 * Gets the cryptographic curve. 222 * 223 * @return The cryptographic curve. 224 */ 225 public Curve getCurve() { 226 227 return crv; 228 } 229 230 231 /** 232 * Gets the 'x' coordinate for the elliptic curve point. It is 233 * represented as the Base64URL encoding of the coordinate's big endian 234 * representation. 235 * 236 * @return The 'x' coordinate. 237 */ 238 public Base64URL getX() { 239 240 return x; 241 } 242 243 244 /** 245 * Gets the 'y' coordinate for the elliptic curve point. It is 246 * represented as the Base64URL encoding of the coordinate's big endian 247 * representation. 248 * 249 * @return The 'y' coordinate. 250 */ 251 public Base64URL getY() { 252 253 return y; 254 } 255 256 257 @Override 258 public JSONObject toJSONObject() { 259 260 JSONObject o = super.toJSONObject(); 261 262 // Append EC specific attributes 263 o.put("crv", crv.toString()); 264 o.put("x", x.toString()); 265 o.put("y", y.toString()); 266 267 return o; 268 } 269 270 271 /** 272 * Parses an Elliptic Curve JWK from the specified JSON object 273 * representation. 274 * 275 * @param jsonObject The JSON object to parse. Must not be 276 * {@code null}. 277 * 278 * @return The Elliptic Curve JWK. 279 * 280 * @throws ParseException If the JSON object couldn't be parsed to a 281 * valid Elliptic Curve JWK. 282 */ 283 public static ECKey parse(final JSONObject jsonObject) 284 throws ParseException { 285 286 // Parse the mandatory parameters first 287 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 288 Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv")); 289 Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x")); 290 Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y")); 291 292 // Get optional key use 293 Use use = JWK.parseKeyUse(jsonObject); 294 295 // Get optional intended algorithm 296 Algorithm alg = JWK.parseAlgorithm(jsonObject); 297 298 // Get optional key ID 299 String id = JWK.parseKeyID(jsonObject); 300 301 // Check key type 302 if (kty != KeyType.EC) 303 throw new ParseException("The key type \"kty\" must be EC", 0); 304 305 return new ECKey(crv, x, y, use, alg, id); 306 } 307 }