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$ (2015-01-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 * Creates a new octet sequence JWK builder. 130 * 131 * @param key The key value. Must not be empty byte array or 132 * {@code null}. 133 */ 134 public Builder(final byte[] key) { 135 136 if (key == null || key.length == 0) { 137 throw new IllegalArgumentException("The key value must not be empty or null"); 138 } 139 140 k = Base64URL.encode(key); 141 } 142 143 144 /** 145 * Sets the use ({@code use}) of the JWK. 146 * 147 * @param use The key use, {@code null} if not specified or if 148 * the key is intended for signing as well as 149 * encryption. 150 * 151 * @return This builder. 152 */ 153 public Builder keyUse(final KeyUse use) { 154 155 this.use = use; 156 return this; 157 } 158 159 160 /** 161 * Sets the operations ({@code key_ops}) of the JWK (for a 162 * non-public key). 163 * 164 * @param ops The key operations, {@code null} if not 165 * specified. 166 * 167 * @return This builder. 168 */ 169 public Builder keyOperations(final Set<KeyOperation> ops) { 170 171 this.ops = ops; 172 return this; 173 } 174 175 176 /** 177 * Sets the intended JOSE algorithm ({@code alg}) for the JWK. 178 * 179 * @param alg The intended JOSE algorithm, {@code null} if not 180 * specified. 181 * 182 * @return This builder. 183 */ 184 public Builder algorithm(final Algorithm alg) { 185 186 this.alg = alg; 187 return this; 188 } 189 190 /** 191 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 192 * to match a specific key. This can be used, for instance, to 193 * choose a key within a {@link JWKSet} during key rollover. 194 * The key ID may also correspond to a JWS/JWE {@code kid} 195 * header parameter value. 196 * 197 * @param kid The key ID, {@code null} if not specified. 198 * 199 * @return This builder. 200 */ 201 public Builder keyID(final String kid) { 202 203 this.kid = kid; 204 return this; 205 } 206 207 208 /** 209 * Sets the X.509 certificate URL ({@code x5u}) of the JWK. 210 * 211 * @param x5u The X.509 certificate URL, {@code null} if not 212 * specified. 213 * 214 * @return This builder. 215 */ 216 public Builder x509CertURL(final URL x5u) { 217 218 this.x5u = x5u; 219 return this; 220 } 221 222 223 /** 224 * Sets the X.509 certificate thumbprint ({@code x5t}) of the 225 * JWK. 226 * 227 * @param x5t The X.509 certificate thumbprint, {@code null} if 228 * not specified. 229 * 230 * @return This builder. 231 */ 232 public Builder x509CertThumbprint(final Base64URL x5t) { 233 234 this.x5t = x5t; 235 return this; 236 } 237 238 /** 239 * Sets the X.509 certificate chain ({@code x5c}) of the JWK. 240 * 241 * @param x5c The X.509 certificate chain as a unmodifiable 242 * list, {@code null} if not specified. 243 * 244 * @return This builder. 245 */ 246 public Builder x509CertChain(final List<Base64> x5c) { 247 248 this.x5c = x5c; 249 return this; 250 } 251 252 /** 253 * Builds a new octet sequence JWK. 254 * 255 * @return The octet sequence JWK. 256 * 257 * @throws IllegalStateException If the JWK parameters were 258 * inconsistently specified. 259 */ 260 public OctetSequenceKey build() { 261 262 try { 263 return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5c); 264 265 } catch (IllegalArgumentException e) { 266 267 throw new IllegalStateException(e.getMessage(), e); 268 } 269 } 270 } 271 272 273 /** 274 * Creates a new octet sequence JSON Web Key (JWK) with the specified 275 * parameters. 276 * 277 * @param k The key value. It is represented as the Base64URL 278 * encoding of value's big endian representation. Must not 279 * be {@code null}. 280 * @param use The key use, {@code null} if not specified or if the key 281 * is intended for signing as well as encryption. 282 * @param ops The key operations, {@code null} if not specified. 283 * @param alg The intended JOSE algorithm for the key, {@code null} if 284 * not specified. 285 * @param kid The key ID. {@code null} if not specified. 286 * @param x5u The X.509 certificate URL, {@code null} if not specified. 287 * @param x5t The X.509 certificate thumbprint, {@code null} if not 288 * specified. 289 * @param x5c The X.509 certificate chain, {@code null} if not 290 * specified. 291 */ 292 public OctetSequenceKey(final Base64URL k, 293 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 294 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 295 296 super(KeyType.OCT, use, ops, alg, kid, x5u, x5t, x5c); 297 298 if (k == null) { 299 throw new IllegalArgumentException("The key value must not be null"); 300 } 301 302 this.k = k; 303 } 304 305 306 /** 307 * Returns the value of this octet sequence key. 308 * 309 * @return The key value. It is represented as the Base64URL encoding 310 * of the coordinate's big endian representation. 311 */ 312 public Base64URL getKeyValue() { 313 314 return k; 315 } 316 317 318 /** 319 * Returns a copy of this octet sequence key value as a byte array. 320 * 321 * @return The key value as a byte array. 322 */ 323 public byte[] toByteArray() { 324 325 return getKeyValue().decode(); 326 } 327 328 329 /** 330 * Octet sequence (symmetric) keys are never considered public, this 331 * method always returns {@code true}. 332 * 333 * @return {@code true} 334 */ 335 @Override 336 public boolean isPrivate() { 337 338 return true; 339 } 340 341 342 /** 343 * Octet sequence (symmetric) keys are never considered public, this 344 * method always returns {@code null}. 345 * 346 * @return {@code null} 347 */ 348 @Override 349 public OctetSequenceKey toPublicJWK() { 350 351 return null; 352 } 353 354 355 @Override 356 public JSONObject toJSONObject() { 357 358 JSONObject o = super.toJSONObject(); 359 360 // Append key value 361 o.put("k", k.toString()); 362 363 return o; 364 } 365 366 367 /** 368 * Parses an octet sequence JWK from the specified JSON object string 369 * representation. 370 * 371 * @param s The JSON object string to parse. Must not be {@code null}. 372 * 373 * @return The octet sequence JWK. 374 * 375 * @throws ParseException If the string couldn't be parsed to an octet 376 * sequence JWK. 377 */ 378 public static OctetSequenceKey parse(final String s) 379 throws ParseException { 380 381 return parse(JSONObjectUtils.parseJSONObject(s)); 382 } 383 384 385 /** 386 * Parses an octet sequence JWK from the specified JSON object 387 * representation. 388 * 389 * @param jsonObject The JSON object to parse. Must not be 390 * @code null}. 391 * 392 * @return The octet sequence JWK. 393 * 394 * @throws ParseException If the JSON object couldn't be parsed to an 395 * octet sequence JWK. 396 */ 397 public static OctetSequenceKey parse(final JSONObject jsonObject) 398 throws ParseException { 399 400 // Parse the mandatory parameters first 401 Base64URL k = new Base64URL(JSONObjectUtils.getString(jsonObject, "k")); 402 403 // Check key type 404 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 405 406 if (kty != KeyType.OCT) { 407 408 throw new ParseException("The key type \"kty\" must be oct", 0); 409 } 410 411 // Get optional key use 412 KeyUse use = null; 413 414 if (jsonObject.containsKey("use")) { 415 use = KeyUse.parse(JSONObjectUtils.getString(jsonObject, "use")); 416 } 417 418 // Get optional key operations 419 Set<KeyOperation> ops = null; 420 421 if (jsonObject.containsKey("key_ops")) { 422 ops = KeyOperation.parse(JSONObjectUtils.getStringList(jsonObject, "key_ops")); 423 } 424 425 // Get optional intended algorithm 426 Algorithm alg = null; 427 428 if (jsonObject.containsKey("alg")) { 429 alg = new Algorithm(JSONObjectUtils.getString(jsonObject, "alg")); 430 } 431 432 // Get optional key ID 433 String kid = null; 434 435 if (jsonObject.containsKey("kid")) { 436 kid = JSONObjectUtils.getString(jsonObject, "kid"); 437 } 438 439 // Get optional X.509 cert URL 440 URL x5u = null; 441 442 if (jsonObject.containsKey("x5u")) { 443 x5u = JSONObjectUtils.getURL(jsonObject, "x5u"); 444 } 445 446 // Get optional X.509 cert thumbprint 447 Base64URL x5t = null; 448 449 if (jsonObject.containsKey("x5t")) { 450 x5t = new Base64URL(JSONObjectUtils.getString(jsonObject, "x5t")); 451 } 452 453 // Get optional X.509 cert chain 454 List<Base64> x5c = null; 455 456 if (jsonObject.containsKey("x5c")) { 457 x5c = X509CertChainUtils.parseX509CertChain(JSONObjectUtils.getJSONArray(jsonObject, "x5c")); 458 } 459 460 return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5c); 461 } 462}