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