001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.auth;
019
020
021import java.net.URI;
022import java.security.PrivateKey;
023import java.security.Provider;
024import java.security.interfaces.ECPrivateKey;
025import java.security.interfaces.RSAPrivateKey;
026import java.util.*;
027
028import net.jcip.annotations.Immutable;
029
030import com.nimbusds.common.contenttype.ContentType;
031import com.nimbusds.jose.JOSEException;
032import com.nimbusds.jose.JWSAlgorithm;
033import com.nimbusds.jose.util.Base64;
034import com.nimbusds.jose.util.Base64URL;
035import com.nimbusds.jwt.SignedJWT;
036import com.nimbusds.oauth2.sdk.ParseException;
037import com.nimbusds.oauth2.sdk.assertions.jwt.JWTAssertionFactory;
038import com.nimbusds.oauth2.sdk.http.HTTPRequest;
039import com.nimbusds.oauth2.sdk.id.Audience;
040import com.nimbusds.oauth2.sdk.id.ClientID;
041import com.nimbusds.oauth2.sdk.id.Issuer;
042import com.nimbusds.oauth2.sdk.util.URLUtils;
043
044
045/**
046 * Private key JWT authentication at the Token endpoint. Implements
047 * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT}.
048 *
049 * <p>Supported signature JSON Web Algorithms (JWAs) by this implementation:
050 *
051 * <ul>
052 *     <li>RS256
053 *     <li>RS384
054 *     <li>RS512
055 *     <li>PS256
056 *     <li>PS384
057 *     <li>PS512
058 *     <li>ES256
059 *     <li>ES256K
060 *     <li>ES384
061 *     <li>ES512
062 * </ul>
063 *
064 * <p>Example {@link com.nimbusds.oauth2.sdk.TokenRequest} with private key JWT
065 * authentication:
066 *
067 * <pre>
068 * POST /token HTTP/1.1
069 * Host: server.example.com
070 * Content-Type: application/x-www-form-urlencoded
071 *
072 * grant_type=authorization_code&amp;
073 * code=i1WsRn1uB1&amp;
074 * client_id=s6BhdRkqt3&amp;
075 * client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&amp;
076 * client_assertion=PHNhbWxwOl...[omitted for brevity]...ZT
077 * </pre>
078 *
079 * <p>Related specifications:
080 *
081 * <ul>
082 *     <li>Assertion Framework for OAuth 2.0 Client Authentication and
083 *         Authorization Grants (RFC 7521).
084 *     <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and
085 *         Authorization Grants (RFC 7523)
086 * </ul>
087 */
088@Immutable
089public final class PrivateKeyJWT extends JWTAuthentication {
090
091
092        /**
093         * Returns the supported signature JSON Web Algorithms (JWAs).
094         *
095         * @return The supported JSON Web Algorithms (JWAs).
096         */
097        public static Set<JWSAlgorithm> supportedJWAs() {
098
099                Set<JWSAlgorithm> supported = new HashSet<>();
100                supported.addAll(JWSAlgorithm.Family.RSA);
101                supported.addAll(JWSAlgorithm.Family.EC);
102                return Collections.unmodifiableSet(supported);
103        }
104
105
106        /**
107         * Creates a new private key JWT authentication. The expiration
108         * time (exp) is set to five minutes from the current system time.
109         * Generates a default identifier (jti) for the JWT. The issued-at
110         * (iat) and not-before (nbf) claims are not set.
111         *
112         * @param clientID     The client identifier. Must not be {@code null}.
113         * @param endpoint     The endpoint URI where the client will submit
114         *                     the JWT authentication, for example the token
115         *                     endpoint. Must not be {@code null}.
116         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
117         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
118         *                     signature algorithm for the JWT assertion. Must
119         *                     be supported and not {@code null}.
120         * @param privateKey   The signing private RSA or EC key. Must not be
121         *                     {@code null}.
122         * @param keyID        Optional identifier for the key, to aid key
123         *                     selection on the recipient side. Recommended.
124         *                     {@code null} if not specified.
125         * @param jcaProvider  Optional specific JCA provider, {@code null} to
126         *                     use the default one.
127         *
128         * @throws JOSEException If RSA signing failed.
129         */
130        public PrivateKeyJWT(final ClientID clientID,
131                             final URI endpoint,
132                             final JWSAlgorithm jwsAlgorithm,
133                             final PrivateKey privateKey,
134                             final String keyID,
135                             final Provider jcaProvider)
136                throws JOSEException {
137
138                this(new JWTAuthenticationClaimsSet(clientID, new Audience(endpoint.toString())),
139                        jwsAlgorithm,
140                        privateKey,
141                        keyID,
142                        null,
143                        null,
144                        jcaProvider);
145        }
146
147
148        /**
149         * Creates a new private key JWT authentication. The expiration
150         * time (exp) is set to five minutes from the current system time.
151         * Generates a default identifier (jti) for the JWT. The issued-at
152         * (iat) and not-before (nbf) claims are not set.
153         *
154         * @param iss          The issuer. May be different from the client
155         *                     identifier. Must not be {@code null}.
156         * @param clientID     The client identifier. Must not be {@code null}.
157         * @param endpoint     The endpoint URI where the client will submit
158         *                     the JWT authentication, for example the token
159         *                     endpoint. Must not be {@code null}.
160         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
161         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
162         *                     signature algorithm for the JWT assertion. Must
163         *                     be supported and not {@code null}.
164         * @param privateKey   The signing private RSA or EC key. Must not be
165         *                     {@code null}.
166         * @param keyID        Optional identifier for the key, to aid key
167         *                     selection on the recipient side. Recommended.
168         *                     {@code null} if not specified.
169         * @param jcaProvider  Optional specific JCA provider, {@code null} to
170         *                     use the default one.
171         *
172         * @throws JOSEException If RSA signing failed.
173         */
174        public PrivateKeyJWT(final Issuer iss,
175                             final ClientID clientID,
176                             final URI endpoint,
177                             final JWSAlgorithm jwsAlgorithm,
178                             final PrivateKey privateKey,
179                             final String keyID,
180                             final Provider jcaProvider)
181                throws JOSEException {
182
183                this(new JWTAuthenticationClaimsSet(iss, clientID, new Audience(endpoint.toString())),
184                        jwsAlgorithm,
185                        privateKey,
186                        keyID,
187                        null,
188                        null,
189                        jcaProvider);
190        }
191
192
193        /**
194         * Creates a new private key JWT authentication. The expiration
195         * time (exp) is set to five minutes from the current system time.
196         * Generates a default identifier (jti) for the JWT. The issued-at
197         * (iat) and not-before (nbf) claims are not set.
198         *
199         * @param clientID     The client identifier. Must not be {@code null}.
200         * @param endpoint     The endpoint URI where the client will submit
201         *                     the JWT authentication, for example the token
202         *                     endpoint. Must not be {@code null}.
203         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
204         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
205         *                     signature algorithm for the JWT assertion. Must
206         *                     be supported and not {@code null}.
207         * @param privateKey   The signing private RSA or EC key. Must not be
208         *                     {@code null}.
209         * @param keyID        Optional identifier for the key, to aid key
210         *                     selection on the recipient side. Recommended.
211         *                     {@code null} if not specified.
212         * @param x5c          Optional X.509 certificate chain for the public
213         *                     key, {@code null} if not specified.
214         * @param x5t256       Optional X.509 certificate SHA-256 thumbprint,
215         *                     {@code null} if not specified.
216         * @param jcaProvider  Optional specific JCA provider, {@code null} to
217         *                     use the default one.
218         *
219         * @throws JOSEException If RSA signing failed.
220         */
221        public PrivateKeyJWT(final ClientID clientID,
222                             final URI endpoint,
223                             final JWSAlgorithm jwsAlgorithm,
224                             final PrivateKey privateKey,
225                             final String keyID,
226                             final List<Base64> x5c,
227                             final Base64URL x5t256,
228                             final Provider jcaProvider)
229                throws JOSEException {
230
231                this(new JWTAuthenticationClaimsSet(clientID, new Audience(endpoint.toString())),
232                        jwsAlgorithm,
233                        privateKey,
234                        keyID,
235                        x5c,
236                        x5t256,
237                        jcaProvider);
238        }
239
240
241        /**
242         * Creates a new private key JWT authentication. The expiration
243         * time (exp) is set to five minutes from the current system time.
244         * Generates a default identifier (jti) for the JWT. The issued-at
245         * (iat) and not-before (nbf) claims are not set.
246         *
247         * @param iss          The issuer. May be different from the client
248         *                     identifier. Must not be {@code null}.
249         * @param clientID     The client identifier. Must not be {@code null}.
250         * @param endpoint     The endpoint URI where the client will submit
251         *                     the JWT authentication, for example the token
252         *                     endpoint. Must not be {@code null}.
253         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
254         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
255         *                     signature algorithm for the JWT assertion. Must
256         *                     be supported and not {@code null}.
257         * @param privateKey   The signing private RSA or EC key. Must not be
258         *                     {@code null}.
259         * @param keyID        Optional identifier for the key, to aid key
260         *                     selection on the recipient side. Recommended.
261         *                     {@code null} if not specified.
262         * @param x5c          Optional X.509 certificate chain for the public
263         *                     key, {@code null} if not specified.
264         * @param x5t256       Optional X.509 certificate SHA-256 thumbprint,
265         *                     {@code null} if not specified.
266         * @param jcaProvider  Optional specific JCA provider, {@code null} to
267         *                     use the default one.
268         *
269         * @throws JOSEException If RSA signing failed.
270         */
271        public PrivateKeyJWT(final Issuer iss,
272                             final ClientID clientID,
273                             final URI endpoint,
274                             final JWSAlgorithm jwsAlgorithm,
275                             final PrivateKey privateKey,
276                             final String keyID,
277                             final List<Base64> x5c,
278                             final Base64URL x5t256,
279                             final Provider jcaProvider)
280                throws JOSEException {
281
282                this(new JWTAuthenticationClaimsSet(iss, clientID, new Audience(endpoint.toString())),
283                        jwsAlgorithm,
284                        privateKey,
285                        keyID,
286                        x5c,
287                        x5t256,
288                        jcaProvider);
289        }
290        
291        
292        /**
293         * Creates a new private key JWT authentication.
294         *
295         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
296         *                         be {@code null}.
297         * @param jwsAlgorithm     The expected RSA (RS256, RS384, RS512,
298         *                         PS256, PS384 or PS512) or EC (ES256, ES384,
299         *                         ES512) signature algorithm for the JWT
300         *                         assertion. Must be supported and not
301         *                         {@code null}.
302         * @param privateKey       The signing private RSA or EC key. Must not
303         *                         be {@code null}.
304         * @param keyID            Optional identifier for the key, to aid key
305         *                         selection on the recipient side.
306         *                         Recommended. {@code null} if not specified.
307         * @param jcaProvider      Optional specific JCA provider, {@code null}
308         *                         to use the default one.
309         *
310         * @throws JOSEException If RSA signing failed.
311         */
312        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
313                             final JWSAlgorithm jwsAlgorithm,
314                             final PrivateKey privateKey,
315                             final String keyID,
316                             final Provider jcaProvider)
317                throws JOSEException {
318                
319                this(jwtAuthClaimsSet, jwsAlgorithm, privateKey, keyID, null, null, jcaProvider);
320        }
321        
322        
323        /**
324         * Creates a new private key JWT authentication.
325         *
326         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
327         *                         be {@code null}.
328         * @param jwsAlgorithm     The expected RSA (RS256, RS384, RS512,
329         *                         PS256, PS384 or PS512) or EC (ES256, ES384,
330         *                         ES512) signature algorithm for the JWT
331         *                         assertion. Must be supported and not
332         *                         {@code null}.
333         * @param privateKey       The signing private RSA or EC key. Must not
334         *                         be {@code null}.
335         * @param keyID            Optional identifier for the key, to aid key
336         *                         selection on the recipient side.
337         *                         Recommended. {@code null} if not specified.
338         * @param x5c              Optional X.509 certificate chain for the
339         *                         public key, {@code null} if not specified.
340         * @param x5t256           Optional X.509 certificate SHA-256
341         *                         thumbprint, {@code null} if not specified.
342         * @param jcaProvider      Optional specific JCA provider, {@code null}
343         *                         to use the default one.
344         *
345         * @throws JOSEException If RSA signing failed.
346         */
347        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
348                             final JWSAlgorithm jwsAlgorithm,
349                             final PrivateKey privateKey,
350                             final String keyID,
351                             final List<Base64> x5c,
352                             final Base64URL x5t256,
353                             final Provider jcaProvider)
354                throws JOSEException {
355                
356                this(JWTAssertionFactory.create(jwtAuthClaimsSet, jwsAlgorithm, privateKey, keyID, x5c, x5t256, jcaProvider));
357        }
358
359
360        /**
361         * Creates a new RSA private key JWT authentication. The expiration
362         * time (exp) is set to five minutes from the current system time.
363         * Generates a default identifier (jti) for the JWT. The issued-at
364         * (iat) and not-before (nbf) claims are not set.
365         *
366         * @param clientID      The client identifier. Must not be
367         *                      {@code null}.
368         * @param endpoint      The endpoint URI where the client will submit
369         *                      the JWT authentication, for example the token
370         *                      endpoint. Must not be {@code null}.
371         * @param jwsAlgorithm  The expected RSA signature algorithm (RS256,
372         *                      RS384 or RS512) for the private key JWT
373         *                      assertion. Must be supported and not
374         *                      {@code null}.
375         * @param rsaPrivateKey The RSA private key. Must not be {@code null}.
376         * @param keyID         Optional identifier for the RSA key, to aid
377         *                      key selection at the authorisation server.
378         *                      Recommended. {@code null} if not specified.
379         * @param jcaProvider   Optional specific JCA provider, {@code null} to
380         *                      use the default one.
381         *
382         * @throws JOSEException If RSA signing failed.
383         */
384        @Deprecated
385        public PrivateKeyJWT(final ClientID clientID,
386                             final URI endpoint,
387                             final JWSAlgorithm jwsAlgorithm,
388                             final RSAPrivateKey rsaPrivateKey,
389                             final String keyID,
390                             final Provider jcaProvider)
391                throws JOSEException {
392
393                this(new JWTAuthenticationClaimsSet(clientID, new Audience(endpoint.toString())),
394                        jwsAlgorithm,
395                        rsaPrivateKey,
396                        keyID,
397                        jcaProvider);
398        }
399
400
401        /**
402         * Creates a new RSA private key JWT authentication.
403         *
404         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
405         *                         be {@code null}.
406         * @param jwsAlgorithm     The expected RSA signature algorithm (RS256,
407         *                         RS384 or RS512) for the private key JWT
408         *                         assertion. Must be supported and not
409         *                         {@code null}.
410         * @param rsaPrivateKey    The RSA private key. Must not be
411         *                         {@code null}.
412         * @param keyID            Optional identifier for the RSA key, to aid
413         *                         key selection at the authorisation server.
414         *                         Recommended. {@code null} if not specified.
415         * @param jcaProvider      Optional specific JCA provider, {@code null}
416         *                         to use the default one.
417         *
418         * @throws JOSEException If RSA signing failed.
419         */
420        @Deprecated
421        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
422                             final JWSAlgorithm jwsAlgorithm,
423                             final RSAPrivateKey rsaPrivateKey,
424                             final String keyID,
425                             final Provider jcaProvider)
426                throws JOSEException {
427
428                this(JWTAssertionFactory.create(jwtAuthClaimsSet, jwsAlgorithm, rsaPrivateKey, keyID, null, null, jcaProvider));
429        }
430
431
432        /**
433         * Creates a new EC private key JWT authentication. The expiration
434         * time (exp) is set to five minutes from the current system time.
435         * Generates a default identifier (jti) for the JWT. The issued-at
436         * (iat) and not-before (nbf) claims are not set.
437         *
438         * @param clientID      The client identifier. Must not be
439         *                      {@code null}.
440         * @param endpoint      The endpoint URI where the client will submit
441         *                      the JWT authentication, for example the token
442         *                      endpoint. Must not be {@code null}.
443         * @param jwsAlgorithm  The expected EC signature algorithm (ES256,
444         *                      ES384 or ES512) for the private key JWT
445         *                      assertion. Must be supported and not
446         *                      {@code null}.
447         * @param ecPrivateKey  The EC private key. Must not be {@code null}.
448         * @param keyID         Optional identifier for the EC key, to aid key
449         *                      selection at the authorisation server.
450         *                      Recommended. {@code null} if not specified.
451         * @param jcaProvider   Optional specific JCA provider, {@code null} to
452         *                      use the default one.
453         *
454         * @throws JOSEException If RSA signing failed.
455         */
456        @Deprecated
457        public PrivateKeyJWT(final ClientID clientID,
458                             final URI endpoint,
459                             final JWSAlgorithm jwsAlgorithm,
460                             final ECPrivateKey ecPrivateKey,
461                             final String keyID,
462                             final Provider jcaProvider)
463                throws JOSEException {
464
465                this(new JWTAuthenticationClaimsSet(clientID, new Audience(endpoint.toString())),
466                        jwsAlgorithm,
467                        ecPrivateKey,
468                        keyID,
469                        jcaProvider);
470        }
471        
472        
473        /**
474         * Creates a new EC private key JWT authentication.
475         *
476         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
477         *                         be {@code null}.
478         * @param jwsAlgorithm     The expected ES signature algorithm (ES256,
479         *                         ES384 or ES512) for the private key JWT
480         *                         assertion. Must be supported and not
481         *                         {@code null}.
482         * @param ecPrivateKey     The EC private key. Must not be
483         *                         {@code null}.
484         * @param keyID            Optional identifier for the EC key, to aid
485         *                         key selection at the authorisation server.
486         *                         Recommended. {@code null} if not specified.
487         * @param jcaProvider      Optional specific JCA provider, {@code null}
488         *                         to use the default one.
489         *
490         * @throws JOSEException If RSA signing failed.
491         */
492        @Deprecated
493        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
494                             final JWSAlgorithm jwsAlgorithm,
495                             final ECPrivateKey ecPrivateKey,
496                             final String keyID,
497                             final Provider jcaProvider)
498                throws JOSEException {
499
500                this(JWTAssertionFactory.create(jwtAuthClaimsSet, jwsAlgorithm, ecPrivateKey, keyID, null, null, jcaProvider));
501        }
502
503
504        /**
505         * Creates a new private key JWT authentication.
506         *
507         * @param clientAssertion The client assertion, corresponding to the
508         *                        {@code client_assertion} parameter, as a
509         *                        supported RSA or ECDSA-signed JWT. Must be
510         *                        signed and not {@code null}.
511         */
512        public PrivateKeyJWT(final SignedJWT clientAssertion) {
513        
514                super(ClientAuthenticationMethod.PRIVATE_KEY_JWT, clientAssertion);
515
516                JWSAlgorithm alg = clientAssertion.getHeader().getAlgorithm();
517
518                if (! JWSAlgorithm.Family.RSA.contains(alg) && ! JWSAlgorithm.Family.EC.contains(alg))
519                        throw new IllegalArgumentException("The client assertion JWT must be RSA or ECDSA-signed (RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 or ES512)");
520        }
521        
522        
523        /**
524         * Parses the specified parameters map for a private key JSON Web Token
525         * (JWT) authentication. Note that the parameters must not be
526         * {@code application/x-www-form-urlencoded} encoded.
527         *
528         * @param params The parameters map to parse. The private key JSON
529         *               Web Token (JWT) parameters must be keyed under 
530         *               "client_assertion" and "client_assertion_type". The 
531         *               map must not be {@code null}.
532         *
533         * @return The private key JSON Web Token (JWT) authentication.
534         *
535         * @throws ParseException If the parameters map couldn't be parsed to a 
536         *                        private key JSON Web Token (JWT) 
537         *                        authentication.
538         */
539        public static PrivateKeyJWT parse(final Map<String,List<String>> params)
540                throws ParseException {
541        
542                JWTAuthentication.ensureClientAssertionType(params);
543                
544                SignedJWT clientAssertion = JWTAuthentication.parseClientAssertion(params);
545
546                PrivateKeyJWT privateKeyJWT;
547
548                try {
549                        privateKeyJWT = new PrivateKeyJWT(clientAssertion);
550
551                }catch (IllegalArgumentException e) {
552
553                        throw new ParseException(e.getMessage(), e);
554                }
555
556                // Check that the top level client_id matches the assertion subject + issuer
557
558                ClientID clientID = JWTAuthentication.parseClientID(params);
559
560                if (clientID != null) {
561
562                        if (! clientID.equals(privateKeyJWT.getClientID()))
563                                throw new ParseException("Invalid private key JWT authentication: The client identifier doesn't match the client assertion subject");
564                }
565
566                return privateKeyJWT;
567        }
568        
569        
570        /**
571         * Parses a private key JSON Web Token (JWT) authentication from the 
572         * specified {@code application/x-www-form-urlencoded} encoded 
573         * parameters string.
574         *
575         * @param paramsString The parameters string to parse. The private key
576         *                     JSON Web Token (JWT) parameters must be keyed 
577         *                     under "client_assertion" and 
578         *                     "client_assertion_type". The string must not be 
579         *                     {@code null}.
580         *
581         * @return The private key JSON Web Token (JWT) authentication.
582         *
583         * @throws ParseException If the parameters string couldn't be parsed 
584         *                        to a private key JSON Web Token (JWT) 
585         *                        authentication.
586         */
587        public static PrivateKeyJWT parse(final String paramsString)
588                throws ParseException {
589                
590                Map<String,List<String>> params = URLUtils.parseParameters(paramsString);
591                
592                return parse(params);
593        }
594        
595        
596        /**
597         * Parses the specified HTTP POST request for a private key JSON Web 
598         * Token (JWT) authentication.
599         *
600         * @param httpRequest The HTTP POST request to parse. Must not be 
601         *                    {@code null} and must contain a valid 
602         *                    {@code application/x-www-form-urlencoded} encoded 
603         *                    parameters string in the entity body. The private 
604         *                    key JSON Web Token (JWT) parameters must be 
605         *                    keyed under "client_assertion" and 
606         *                    "client_assertion_type".
607         *
608         * @return The private key JSON Web Token (JWT) authentication.
609         *
610         * @throws ParseException If the HTTP request header couldn't be parsed
611         *                        to a private key JSON Web Token (JWT) 
612         *                        authentication.
613         */
614        public static PrivateKeyJWT parse(final HTTPRequest httpRequest)
615                throws ParseException {
616                
617                httpRequest.ensureMethod(HTTPRequest.Method.POST);
618                httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED);
619                
620                return parse(httpRequest.getQueryParameters());
621        }
622}