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