001package com.nimbusds.jose.jwk; 002 003 004import java.net.URL; 005import java.util.List; 006import java.text.ParseException; 007import java.util.Set; 008 009import net.jcip.annotations.Immutable; 010 011import net.minidev.json.JSONObject; 012 013import com.nimbusds.jose.Algorithm; 014import com.nimbusds.jose.util.Base64; 015import com.nimbusds.jose.util.Base64URL; 016import com.nimbusds.jose.util.JSONObjectUtils; 017import com.nimbusds.jose.util.X509CertChainUtils; 018 019 020/** 021 * {@link KeyType#OCT Octet sequence} JSON Web Key (JWK), used to represent 022 * symmetric keys. This class is immutable. 023 * 024 * <p>Example JSON object representation of an octet sequence JWK: 025 * 026 * <pre> 027 * { 028 * "kty" : "oct", 029 * "alg" : "A128KW", 030 * "k" : "GawgguFyGrWKav7AX4VKUg" 031 * } 032 * </pre> 033 * 034 * @author Justin Richer 035 * @author Vladimir Dzhuvinov 036 * @version $version$ (2014-04-20) 037 */ 038@Immutable 039public final class OctetSequenceKey extends JWK { 040 041 042 /** 043 * The symmetric key value. 044 */ 045 private final Base64URL k; 046 047 048 /** 049 * Builder for constructing octet sequence JWKs. 050 * 051 * <p>Example use: 052 * 053 * <pre> 054 * OctetSequenceKey key = new OctetSequenceKey.Builder(k). 055 * algorithm(JWSAlgorithm.HS512). 056 * keyID("123"). 057 * build(); 058 * </pre> 059 */ 060 public static class Builder { 061 062 063 /** 064 * The symmetric key value. 065 */ 066 private final Base64URL k; 067 068 069 /** 070 * The public key use, optional. 071 */ 072 private KeyUse use; 073 074 075 /** 076 * The key operations, optional. 077 */ 078 private Set<KeyOperation> ops; 079 080 081 /** 082 * The intended JOSE algorithm for the key, optional. 083 */ 084 private Algorithm alg; 085 086 087 /** 088 * The key ID, optional. 089 */ 090 private String kid; 091 092 093 /** 094 * X.509 certificate URL, optional. 095 */ 096 private URL x5u; 097 098 099 /** 100 * X.509 certificate thumbprint, optional. 101 */ 102 private Base64URL x5t; 103 104 105 /** 106 * The X.509 certificate chain, optional. 107 */ 108 private List<Base64> x5c; 109 110 111 /** 112 * Creates a new octet sequence JWK builder. 113 * 114 * @param k The key value. It is represented as the Base64URL 115 * encoding of value's big endian representation. Must 116 * not be {@code null}. 117 */ 118 public Builder(final Base64URL k) { 119 120 if (k == null) { 121 throw new IllegalArgumentException("The key value must not be null"); 122 } 123 124 this.k = k; 125 } 126 127 128 /** 129 * Sets the use ({@code use}) of the JWK. 130 * 131 * @param use The key use, {@code null} if not specified or if 132 * the key is intended for signing as well as 133 * encryption. 134 * 135 * @return This builder. 136 */ 137 public Builder keyUse(final KeyUse use) { 138 139 this.use = use; 140 return this; 141 } 142 143 144 /** 145 * Sets the operations ({@code key_ops}) of the JWK (for a 146 * non-public key). 147 * 148 * @param ops The key operations, {@code null} if not 149 * specified. 150 * 151 * @return This builder. 152 */ 153 public Builder keyOperations(final Set<KeyOperation> ops) { 154 155 this.ops = ops; 156 return this; 157 } 158 159 160 /** 161 * Sets the intended JOSE algorithm ({@code alg}) for the JWK. 162 * 163 * @param alg The intended JOSE algorithm, {@code null} if not 164 * specified. 165 * 166 * @return This builder. 167 */ 168 public Builder algorithm(final Algorithm alg) { 169 170 this.alg = alg; 171 return this; 172 } 173 174 /** 175 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 176 * to match a specific key. This can be used, for instance, to 177 * choose a key within a {@link JWKSet} during key rollover. 178 * The key ID may also correspond to a JWS/JWE {@code kid} 179 * header parameter value. 180 * 181 * @param kid The key ID, {@code null} if not specified. 182 * 183 * @return This builder. 184 */ 185 public Builder keyID(final String kid) { 186 187 this.kid = kid; 188 return this; 189 } 190 191 192 /** 193 * Sets the X.509 certificate URL ({@code x5u}) of the JWK. 194 * 195 * @param x5u The X.509 certificate URL, {@code null} if not 196 * specified. 197 * 198 * @return This builder. 199 */ 200 public Builder x509CertURL(final URL x5u) { 201 202 this.x5u = x5u; 203 return this; 204 } 205 206 207 /** 208 * Sets the X.509 certificate thumbprint ({@code x5t}) of the 209 * JWK. 210 * 211 * @param x5t The X.509 certificate thumbprint, {@code null} if 212 * not specified. 213 * 214 * @return This builder. 215 */ 216 public Builder x509CertThumbprint(final Base64URL x5t) { 217 218 this.x5t = x5t; 219 return this; 220 } 221 222 /** 223 * Sets the X.509 certificate chain ({@code x5c}) of the JWK. 224 * 225 * @param x5c The X.509 certificate chain as a unmodifiable 226 * list, {@code null} if not specified. 227 * 228 * @return This builder. 229 */ 230 public Builder x509CertChain(final List<Base64> x5c) { 231 232 this.x5c = x5c; 233 return this; 234 } 235 236 /** 237 * Builds a new octet sequence JWK. 238 * 239 * @return The octet sequence JWK. 240 * 241 * @throws IllegalStateException If the JWK parameters were 242 * inconsistently specified. 243 */ 244 public OctetSequenceKey build() { 245 246 try { 247 return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5c); 248 249 } catch (IllegalArgumentException e) { 250 251 throw new IllegalStateException(e.getMessage(), e); 252 } 253 } 254 } 255 256 257 /** 258 * Creates a new octet sequence JSON Web Key (JWK) with the specified 259 * parameters. 260 * 261 * @param k The key value. It is represented as the Base64URL 262 * encoding of value's big endian representation. Must not 263 * be {@code null}. 264 * @param use The key use, {@code null} if not specified or if the key 265 * is intended for signing as well as encryption. 266 * @param ops The key operations, {@code null} if not specified. 267 * @param alg The intended JOSE algorithm for the key, {@code null} if 268 * not specified. 269 * @param kid The key ID. {@code null} if not specified. 270 * @param x5u The X.509 certificate URL, {@code null} if not specified. 271 * @param x5t The X.509 certificate thumbprint, {@code null} if not 272 * specified. 273 * @param x5c The X.509 certificate chain, {@code null} if not 274 * specified. 275 */ 276 public OctetSequenceKey(final Base64URL k, 277 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 278 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 279 280 super(KeyType.OCT, use, ops, alg, kid, x5u, x5t, x5c); 281 282 if (k == null) { 283 throw new IllegalArgumentException("The key value must not be null"); 284 } 285 286 this.k = k; 287 } 288 289 290 /** 291 * Returns the value of this octet sequence key. 292 * 293 * @return The key value. It is represented as the Base64URL encoding 294 * of the coordinate's big endian representation. 295 */ 296 public Base64URL getKeyValue() { 297 298 return k; 299 } 300 301 302 /** 303 * Returns a copy of this octet sequence key value as a byte array. 304 * 305 * @return The key value as a byte array. 306 */ 307 public byte[] toByteArray() { 308 309 return getKeyValue().decode(); 310 } 311 312 313 /** 314 * Octet sequence (symmetric) keys are never considered public, this 315 * method always returns {@code true}. 316 * 317 * @return {@code true} 318 */ 319 @Override 320 public boolean isPrivate() { 321 322 return true; 323 } 324 325 326 /** 327 * Octet sequence (symmetric) keys are never considered public, this 328 * method always returns {@code null}. 329 * 330 * @return {@code null} 331 */ 332 @Override 333 public OctetSequenceKey toPublicJWK() { 334 335 return null; 336 } 337 338 339 @Override 340 public JSONObject toJSONObject() { 341 342 JSONObject o = super.toJSONObject(); 343 344 // Append key value 345 o.put("k", k.toString()); 346 347 return o; 348 } 349 350 351 /** 352 * Parses an octet sequence JWK from the specified JSON object string 353 * representation. 354 * 355 * @param s The JSON object string to parse. Must not be {@code null}. 356 * 357 * @return The octet sequence JWK. 358 * 359 * @throws ParseException If the string couldn't be parsed to an octet 360 * sequence JWK. 361 */ 362 public static OctetSequenceKey parse(final String s) 363 throws ParseException { 364 365 return parse(JSONObjectUtils.parseJSONObject(s)); 366 } 367 368 369 /** 370 * Parses an octet sequence JWK from the specified JSON object 371 * representation. 372 * 373 * @param jsonObject The JSON object to parse. Must not be 374 * @code null}. 375 * 376 * @return The octet sequence JWK. 377 * 378 * @throws ParseException If the JSON object couldn't be parsed to an 379 * octet sequence JWK. 380 */ 381 public static OctetSequenceKey parse(final JSONObject jsonObject) 382 throws ParseException { 383 384 // Parse the mandatory parameters first 385 Base64URL k = new Base64URL(JSONObjectUtils.getString(jsonObject, "k")); 386 387 // Check key type 388 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 389 390 if (kty != KeyType.OCT) { 391 392 throw new ParseException("The key type \"kty\" must be oct", 0); 393 } 394 395 // Get optional key use 396 KeyUse use = null; 397 398 if (jsonObject.containsKey("use")) { 399 use = KeyUse.parse(JSONObjectUtils.getString(jsonObject, "use")); 400 } 401 402 // Get optional key operations 403 Set<KeyOperation> ops = null; 404 405 if (jsonObject.containsKey("key_ops")) { 406 ops = KeyOperation.parse(JSONObjectUtils.getStringList(jsonObject, "key_ops")); 407 } 408 409 // Get optional intended algorithm 410 Algorithm alg = null; 411 412 if (jsonObject.containsKey("alg")) { 413 alg = new Algorithm(JSONObjectUtils.getString(jsonObject, "alg")); 414 } 415 416 // Get optional key ID 417 String kid = null; 418 419 if (jsonObject.containsKey("kid")) { 420 kid = JSONObjectUtils.getString(jsonObject, "kid"); 421 } 422 423 // Get optional X.509 cert URL 424 URL x5u = null; 425 426 if (jsonObject.containsKey("x5u")) { 427 x5u = JSONObjectUtils.getURL(jsonObject, "x5u"); 428 } 429 430 // Get optional X.509 cert thumbprint 431 Base64URL x5t = null; 432 433 if (jsonObject.containsKey("x5t")) { 434 x5t = new Base64URL(JSONObjectUtils.getString(jsonObject, "x5t")); 435 } 436 437 // Get optional X.509 cert chain 438 List<Base64> x5c = null; 439 440 if (jsonObject.containsKey("x5c")) { 441 x5c = X509CertChainUtils.parseX509CertChain(JSONObjectUtils.getJSONArray(jsonObject, "x5c")); 442 } 443 444 return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5c); 445 } 446}