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.openid.connect.sdk.federation.entities;
019
020
021import java.util.Date;
022import java.util.LinkedList;
023import java.util.List;
024
025import net.minidev.json.JSONObject;
026
027import com.nimbusds.jose.jwk.JWKSet;
028import com.nimbusds.jwt.JWTClaimsSet;
029import com.nimbusds.oauth2.sdk.ParseException;
030import com.nimbusds.oauth2.sdk.as.AuthorizationServerMetadata;
031import com.nimbusds.oauth2.sdk.client.ClientMetadata;
032import com.nimbusds.oauth2.sdk.id.Identifier;
033import com.nimbusds.oauth2.sdk.id.Issuer;
034import com.nimbusds.oauth2.sdk.id.Subject;
035import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
036import com.nimbusds.oauth2.sdk.util.MapUtils;
037import com.nimbusds.openid.connect.sdk.claims.CommonClaimsSet;
038import com.nimbusds.openid.connect.sdk.federation.trust.constraints.TrustChainConstraints;
039import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
040import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
041
042
043/**
044 * Federation entity statement claims set, serialisable to a JSON object.
045 *
046 * <p>Example claims set:
047 *
048 * <pre>
049 * {
050 *   "iss": "https://feide.no",
051 *   "sub": "https://ntnu.no",
052 *   "iat": 1516239022,
053 *   "exp": 1516298022,
054 *   "crit": ["jti"],
055 *   "jti": "7l2lncFdY6SlhNia",
056 *   "policy_language_crit": ["regexp"],
057 *   "metadata_policy": {
058 *     "openid_provider": {
059 *       "issuer": {"value": "https://ntnu.no"},
060 *       "organization_name": {"value": "NTNU"},
061 *       "id_token_signing_alg_values_supported":
062 *         {"subset_of": ["RS256", "RS384", "RS512"]},
063 *       "op_policy_uri": {
064 *         "regexp": "^https:\/\/[\w-]+\.example\.com\/[\w-]+\.html"}
065 *     },
066 *     "openid_relying_party": {
067 *       "organization_name": {"value": "NTNU"},
068 *       "grant_types_supported": {
069 *         "subset_of": ["authorization_code", "implicit"]},
070 *       "scopes": {
071 *         "subset_of": ["openid", "profile", "email", "phone"]}
072 *     }
073 *   },
074 *   "constraints": {
075 *     "max_path_length": 2
076 *   }
077 *   "jwks": {
078 *     "keys": [
079 *       {
080 *         "alg": "RS256",
081 *         "e": "AQAB",
082 *         "ext": true,
083 *         "key_ops": ["verify"],
084 *         "kid": "key1",
085 *         "kty": "RSA",
086 *         "n": "pnXBOusEANuug6ewezb9J_...",
087 *         "use": "sig"
088 *       }
089 *     ]
090 *   },
091 *   "authority_hints": [
092 *     "https://edugain.org/federation"
093 *   ]
094 * }
095 * </pre>
096 *
097 * <p>Related specifications:
098 *
099 * <ul>
100 *     <li>OpenID Connect Federation 1.0, section 2.1.
101 * </ul>
102 */
103public class EntityStatementClaimsSet extends CommonClaimsSet {
104        
105        
106        /**
107         * The expiration time claim name.
108         */
109        public static final String EXP_CLAIM_NAME = "exp";
110        
111        
112        /**
113         * The JWK set claim name.
114         */
115        public static final String JWKS_CLAIM_NAME = "jwks";
116        
117        
118        /**
119         * The authority hints claim name.
120         */
121        public static final String AUTHORITY_HINTS_CLAIM_NAME = "authority_hints";
122        
123        
124        /**
125         * The metadata claim name.
126         */
127        public static final String METADATA_CLAIM_NAME = "metadata";
128        
129        
130        /**
131         * The metadata policy claim name.
132         */
133        public static final String METADATA_POLICY_CLAIM_NAME = "metadata_policy";
134        
135        
136        /**
137         * The constraints claim name.
138         */
139        public static final String CONSTRAINTS_CLAIM_NAME = "constraints";
140        
141        
142        /**
143         * The critical claim name.
144         */
145        public static final String CRITICAL_CLAIM_NAME = "crit";
146        
147        
148        /**
149         * The policy critical claim name.
150         */
151        public static final String POLICY_LANGUAGE_CRITICAL_CLAIM_NAME = "policy_language_crit";
152        
153        
154        /**
155         * Creates a new federation entity statement claims set with the
156         * minimum required claims.
157         *
158         * @param iss  The issuer. Must not be {@code null}.
159         * @param sub  The subject. Must not be {@code null}.
160         * @param iat  The issue time. Must not be {@code null}.
161         * @param exp  The expiration time. Must not be {@code null}.
162         * @param jwks The entity public JWK set. Must not be {@code null}.
163         */
164        public EntityStatementClaimsSet(final Issuer iss,
165                                        final Subject sub,
166                                        final Date iat,
167                                        final Date exp,
168                                        final JWKSet jwks) {
169                
170                this(new EntityID(iss.getValue()), new EntityID(sub.getValue()), iat, exp, jwks);
171        }
172        
173        
174        /**
175         * Creates a new federation entity statement claims set with the
176         * minimum required claims.
177         *
178         * @param iss  The issuer. Must not be {@code null}.
179         * @param sub  The subject. Must not be {@code null}.
180         * @param iat  The issue time. Must not be {@code null}.
181         * @param exp  The expiration time. Must not be {@code null}.
182         * @param jwks The entity public JWK set. Must not be {@code null}.
183         */
184        public EntityStatementClaimsSet(final EntityID iss,
185                                        final EntityID sub,
186                                        final Date iat,
187                                        final Date exp,
188                                        final JWKSet jwks) {
189                
190                setClaim(ISS_CLAIM_NAME, iss.getValue());
191                setClaim(SUB_CLAIM_NAME, sub.getValue());
192                setDateClaim(IAT_CLAIM_NAME, iat);
193                setDateClaim(EXP_CLAIM_NAME, exp);
194                setClaim(JWKS_CLAIM_NAME, jwks.toJSONObject(true)); // public JWKs only
195        }
196        
197        
198        /**
199         * Creates a new federation entity statement claims set from the
200         * specified JWT claims set.
201         *
202         * @param jwtClaimsSet The JWT claims set. Must not be {@code null}.
203         *
204         * @throws ParseException If the JWT claims set doesn't represent a
205         *                        valid federation entity statement claims set.
206         */
207        public EntityStatementClaimsSet(final JWTClaimsSet jwtClaimsSet)
208                throws ParseException {
209                
210                super(jwtClaimsSet.toJSONObject());
211                
212                validateRequiredClaimsPresence();
213        }
214        
215        
216        /**
217         * Validates this claims set for having all minimum required claims for
218         * an entity statement. If a {@link #isSelfStatement() selt-statement}
219         * check for the {@link #hasMetadata() presence of metadata}. If
220         * {@link #getCriticalExtensionClaims() critical extension claims} are
221         * listed their presence is also checked.
222         *
223         * @throws ParseException If the validation failed and a required claim
224         *                        is missing.
225         */
226        public void validateRequiredClaimsPresence()
227                throws ParseException {
228                
229                if (getIssuer() == null) {
230                        throw new ParseException("Missing iss (issuer) claim");
231                }
232                
233                EntityID.parse(getIssuer()); // ensure URI
234                
235                if (getSubject() == null) {
236                        throw new ParseException("Missing sub (subject) claim");
237                }
238                
239                EntityID.parse(getSubject()); // ensure URI
240                
241                if (getIssueTime() == null) {
242                        throw new ParseException("Missing iat (issued-at) claim");
243                }
244                
245                if (getExpirationTime() == null) {
246                        throw new ParseException("Missing exp (expiration) claim");
247                }
248                
249                if (getJWKSet() == null) {
250                        throw new ParseException("Missing jwks (JWK set) claim");
251                }
252                
253                if (isSelfStatement() && ! hasMetadata()) {
254                        throw new ParseException("Missing required metadata claim for self-statement");
255                }
256                
257                List<String> crit = getCriticalExtensionClaims();
258                
259                if (crit != null) {
260                        for (String claimName: crit) {
261                                if (getClaim(claimName) == null) {
262                                        throw new ParseException("Missing critical " + claimName + " claim");
263                                }
264                        }
265                }
266        }
267        
268        
269        /**
270         * Returns {@code true} if this is a self-statement (issuer and subject
271         * match).
272         *
273         * @return {@code true} for a self-statement, {@code false} if not.
274         */
275        public boolean isSelfStatement() {
276                
277                Issuer issuer = getIssuer();
278                Subject subject = getSubject();
279                
280                return issuer != null && subject != null && issuer.getValue().equals(subject.getValue());
281        }
282        
283        
284        /**
285         * Returns the issuer as entity ID.
286         *
287         * @return The issuer as entity ID.
288         */
289        public EntityID getIssuerEntityID() {
290                
291                return new EntityID(getIssuer().getValue());
292        }
293        
294        
295        /**
296         * Returns the subject as entity ID.
297         *
298         * @return The subject as entity ID.
299         */
300        public EntityID getSubjectEntityID() {
301                
302                return new EntityID(getSubject().getValue());
303        }
304        
305        
306        /**
307         * Gets the entity statement expiration time. Corresponds to the
308         * {@code exp} claim.
309         *
310         * @return The expiration time, {@code null} if not specified or
311         *         parsing failed.
312         */
313        public Date getExpirationTime() {
314                
315                return getDateClaim(EXP_CLAIM_NAME);
316        }
317        
318        
319        /**
320         * Gets the entity JWK set.
321         *
322         * @return The entity JWK set, {@code null} if not specified or parsing
323         *         failed.
324         */
325        public JWKSet getJWKSet() {
326                
327                try {
328                        return JWKSet.parse(getJSONObjectClaim(JWKS_CLAIM_NAME));
329                } catch (java.text.ParseException e) {
330                        return null;
331                }
332        }
333        
334        
335        /**
336         * Gets the entity IDs of the intermediate entities or trust anchors.
337         *
338         * @return The entity IDs, {@code null} or empty list for a trust
339         *         anchor, or if parsing failed.
340         */
341        public List<EntityID> getAuthorityHints() {
342                
343                List<String> strings = getStringListClaim(AUTHORITY_HINTS_CLAIM_NAME);
344                
345                if (strings == null) {
346                        return null;
347                }
348                
349                List<EntityID> trustChain = new LinkedList<>();
350                for (String s: strings) {
351                        trustChain.add(new EntityID(s));
352                }
353                return trustChain;
354        }
355        
356        
357        /**
358         * Sets the entity IDs of the intermediate entities or trust anchors.
359         *
360         * @param trustChain The entity IDs, {@code null} or empty list for a
361         *                   trust anchor.
362         */
363        public void setAuthorityHints(final List<EntityID> trustChain) {
364                
365                if (trustChain != null) {
366                        setClaim(AUTHORITY_HINTS_CLAIM_NAME, Identifier.toStringList(trustChain));
367                } else {
368                        setClaim(AUTHORITY_HINTS_CLAIM_NAME, null);
369                }
370        }
371        
372        
373        /**
374         * Returns {@code true} if a metadata field is present.
375         *
376         * @return {@code true} if for a metadata field for an OpenID relying
377         *         party, OpenID provider, OAuth authorisation server, OAuth
378         *         client, OAuth protected resource or a federation entity is
379         *         present.
380         */
381        public boolean hasMetadata() {
382        
383                JSONObject metadataObject = getJSONObjectClaim(METADATA_CLAIM_NAME);
384                
385                if (MapUtils.isEmpty(metadataObject)) {
386                        return false;
387                }
388                
389                if (metadataObject.get(FederationMetadataType.OPENID_RELYING_PARTY.getValue()) != null) return true;
390                if (metadataObject.get(FederationMetadataType.OPENID_PROVIDER.getValue()) != null) return true;
391                if (metadataObject.get(FederationMetadataType.OAUTH_AUTHORIZATION_SERVER.getValue()) != null) return true;
392                if (metadataObject.get(FederationMetadataType.OAUTH_CLIENT.getValue()) != null) return true;
393                if (metadataObject.get(FederationMetadataType.OAUTH_RESOURCE.getValue()) != null) return true;
394                if (metadataObject.get(FederationMetadataType.FEDERATION_ENTITY.getValue()) != null) return true;
395                
396                return false;
397        }
398        
399        
400        /**
401         * Gets the OpenID relying party metadata if present for this entity.
402         *
403         * @return The RP metadata, {@code null} if not specified or if parsing
404         *         failed.
405         */
406        public OIDCClientMetadata getRPMetadata() {
407                
408                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
409                
410                if (o == null) {
411                        return null;
412                }
413                
414                try {
415                        JSONObject rpo = JSONObjectUtils.getJSONObject(o, FederationMetadataType.OPENID_RELYING_PARTY.getValue(), null);
416                        if (rpo == null) {
417                                return null;
418                        }
419                        return OIDCClientMetadata.parse(rpo);
420                        
421                } catch (com.nimbusds.oauth2.sdk.ParseException e) {
422                        return null;
423                }
424        }
425        
426        
427        /**
428         * Sets the OpenID relying party metadata if present for this entity.
429         *
430         * @param rpMetadata The RP metadata, {@code null} if not specified.
431         */
432        public void setRPMetadata(final OIDCClientMetadata rpMetadata) {
433                
434                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
435                
436                if (o == null) {
437                        if (rpMetadata == null) {
438                                return; // nothing to clear
439                        }
440                        o = new JSONObject();
441                }
442                
443                if (rpMetadata != null) {
444                        o.put(FederationMetadataType.OPENID_RELYING_PARTY.getValue(), rpMetadata.toJSONObject());
445                } else {
446                        o.put(FederationMetadataType.OPENID_RELYING_PARTY.getValue(), null);
447                }
448                
449                setClaim(METADATA_CLAIM_NAME, o);
450        }
451        
452        
453        /**
454         * Gets the OpenID provider metadata if present for this entity.
455         *
456         * @return The OP metadata, {@code null} if not specified or if parsing
457         *         failed.
458         */
459        public OIDCProviderMetadata getOPMetadata() {
460                
461                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
462                
463                if (o == null) {
464                        return null;
465                }
466                
467                try {
468                        JSONObject opo = JSONObjectUtils.getJSONObject(o, FederationMetadataType.OPENID_PROVIDER.getValue(), null);
469                        if (opo == null) {
470                                return null;
471                        }
472                        return OIDCProviderMetadata.parse(opo);
473                        
474                } catch (com.nimbusds.oauth2.sdk.ParseException e) {
475                        return null;
476                }
477        }
478        
479        
480        /**
481         * Gets the OpenID provider metadata if present for this entity.
482         *
483         * @param opMetadata The OP metadata, {@code null} if not specified.
484         */
485        public void setOPMetadata(final OIDCProviderMetadata opMetadata) {
486                
487                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
488                
489                if (o == null) {
490                        if (opMetadata == null) {
491                                return; // nothing to clear
492                        }
493                        o = new JSONObject();
494                }
495                
496                if (opMetadata != null) {
497                        o.put(FederationMetadataType.OPENID_PROVIDER.getValue(), opMetadata.toJSONObject());
498                } else {
499                        o.put(FederationMetadataType.OPENID_PROVIDER.getValue(), null);
500                }
501                
502                setClaim(METADATA_CLAIM_NAME, o);
503        }
504        
505        
506        /**
507         * Gets the OAuth 2.0 client metadata if present for this entity.
508         *
509         * @return The client metadata, {@code null} if not specified or if
510         *         parsing failed.
511         */
512        public ClientMetadata getOAuthClientMetadata() {
513                
514                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
515                
516                if (o == null) {
517                        return null;
518                }
519                
520                try {
521                        JSONObject aco = JSONObjectUtils.getJSONObject(o, FederationMetadataType.OAUTH_CLIENT.getValue(), null);
522                        if (aco == null) {
523                                return null;
524                        }
525                        return ClientMetadata.parse(aco);
526                        
527                } catch (com.nimbusds.oauth2.sdk.ParseException e) {
528                        return null;
529                }
530        }
531        
532        
533        /**
534         * Sets the OAuth 2.0 client metadata if present for this entity.
535         *
536         * @param clientMetadata The client metadata, {@code null} if not
537         *                       specified.
538         */
539        public void setOAuthClientMetadata(final ClientMetadata clientMetadata) {
540                
541                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
542                
543                if (o == null) {
544                        if (clientMetadata == null) {
545                                return; // nothing to clear
546                        }
547                        o = new JSONObject();
548                }
549                
550                if (clientMetadata != null) {
551                        o.put(FederationMetadataType.OAUTH_CLIENT.getValue(), clientMetadata.toJSONObject());
552                } else {
553                        o.put(FederationMetadataType.OAUTH_CLIENT.getValue(), null);
554                }
555                
556                setClaim(METADATA_CLAIM_NAME, o);
557        }
558        
559        
560        /**
561         * Gets the OAuth 2.0 authorisation server metadata if present for this
562         * entity.
563         *
564         * @return The AS metadata, {@code null} if not specified or if parsing
565         *         failed.
566         */
567        public AuthorizationServerMetadata getASMetadata() {
568                
569                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
570                
571                if (o == null) {
572                        return null;
573                }
574                
575                try {
576                        JSONObject opo = JSONObjectUtils.getJSONObject(o, FederationMetadataType.OAUTH_AUTHORIZATION_SERVER.getValue(), null);
577                        if (opo == null) {
578                                return null;
579                        }
580                        return AuthorizationServerMetadata.parse(opo);
581                        
582                } catch (com.nimbusds.oauth2.sdk.ParseException e) {
583                        return null;
584                }
585        }
586        
587        
588        /**
589         * Sets the OAuth 2.0 authorisation server metadata if present for this
590         * entity.
591         *
592         * @param asMetadata The AS metadata, {@code null} if not specified.
593         */
594        public void setASMetadata(final AuthorizationServerMetadata asMetadata) {
595                
596                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
597                
598                if (o == null) {
599                        if (asMetadata == null) {
600                                return; // nothing to clear
601                        }
602                        o = new JSONObject();
603                }
604                
605                if (asMetadata != null) {
606                        o.put(FederationMetadataType.OAUTH_AUTHORIZATION_SERVER.getValue(), asMetadata.toJSONObject());
607                } else {
608                        o.put(FederationMetadataType.OAUTH_AUTHORIZATION_SERVER.getValue(), null);
609                }
610                
611                setClaim(METADATA_CLAIM_NAME, o);
612        }
613        
614        
615        /**
616         * Gets the federation entity metadata if present for this entity.
617         *
618         * @return The federation entity metadata, {@code null} if not
619         *         specified or if parsing failed.
620         */
621        public FederationEntityMetadata getFederationEntityMetadata() {
622                
623                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
624                
625                if (o == null) {
626                        return null;
627                }
628                
629                try {
630                        JSONObject feo = JSONObjectUtils.getJSONObject(o, FederationMetadataType.FEDERATION_ENTITY.getValue(), null);
631                        if (feo == null) {
632                                return null;
633                        }
634                        return FederationEntityMetadata.parse(feo);
635                        
636                } catch (com.nimbusds.oauth2.sdk.ParseException e) {
637                        return null;
638                }
639        }
640        
641        
642        /**
643         * Sets the federation entity metadata if present for this entity.
644         *
645         * @param entityMetadata The federation entity metadata, {@code null}
646         *                       if not specified.
647         */
648        public void setFederationEntityMetadata(final FederationEntityMetadata entityMetadata) {
649                
650                JSONObject o = getJSONObjectClaim(METADATA_CLAIM_NAME);
651                
652                if (o == null) {
653                        if (entityMetadata == null) {
654                                return; // nothing to clear
655                        }
656                        o = new JSONObject();
657                }
658                
659                if (entityMetadata != null) {
660                        o.put(FederationMetadataType.FEDERATION_ENTITY.getValue(), entityMetadata.toJSONObject());
661                } else {
662                        o.put(FederationMetadataType.FEDERATION_ENTITY.getValue(), null);
663                }
664                
665                setClaim(METADATA_CLAIM_NAME, o);
666        }
667        
668        
669        /**
670         * Gets the metadata policy JSON object.
671         *
672         * @return The metadata policy JSON object, {@code null} if not
673         *         specified or if parsing failed.
674         */
675        public JSONObject getMetadataPolicyJSONObject() {
676                
677                return getJSONObjectClaim(METADATA_POLICY_CLAIM_NAME);
678        }
679        
680        
681        /**
682         * Sets the metadata policy JSON object.
683         *
684         * @param metadataPolicy The metadata policy JSON object, {@code null}
685         *                       if not specified.
686         */
687        public void setMetadataPolicyJSONObject(final JSONObject metadataPolicy) {
688        
689                setClaim(METADATA_POLICY_CLAIM_NAME, metadataPolicy);
690        }
691        
692        
693        /**
694         * Gets the trust chain constraints for subordinate entities.
695         *
696         * @return The trust chain constraints, {@code null} if not specified
697         *          or if parsing failed.
698         */
699        public TrustChainConstraints getConstraints() {
700                
701                JSONObject o = getJSONObjectClaim(CONSTRAINTS_CLAIM_NAME);
702                
703                if (o == null) {
704                        return null;
705                }
706                
707                try {
708                        return TrustChainConstraints.parse(o);
709                } catch (com.nimbusds.oauth2.sdk.ParseException e) {
710                        return null;
711                }
712        }
713        
714        
715        /**
716         * Sets the trust chain constraints for subordinate entities.
717         *
718         * @param constraints The trust chain constraints, {@code null} if not
719         *                    specified.
720         */
721        public void setConstraints(final TrustChainConstraints constraints) {
722        
723                if (constraints != null) {
724                        setClaim(CONSTRAINTS_CLAIM_NAME, constraints.toJSONObject());
725                } else {
726                        setClaim(CONSTRAINTS_CLAIM_NAME, null);
727                }
728        }
729        
730        
731        /**
732         * Gets the names of the critical extension claims.
733         *
734         * @return The names of the critical extension claims, {@code null} if
735         *         not specified or if parsing failed.
736         */
737        public List<String> getCriticalExtensionClaims() {
738                
739                return getStringListClaim(CRITICAL_CLAIM_NAME);
740        }
741        
742        
743        /**
744         * Sets the names of the critical extension claims.
745         *
746         * @param claimNames The names of the critical extension claims,
747         *                   {@code null} if not specified. Must not be an
748         *                   empty list.
749         */
750        public void setCriticalExtensionClaims(final List<String> claimNames) {
751        
752                if (claimNames != null && claimNames.isEmpty()) {
753                        throw new IllegalArgumentException("The critical extension claim names must not be empty");
754                }
755                
756                setClaim(CRITICAL_CLAIM_NAME, claimNames);
757        }
758        
759        
760        /**
761         * Gets the names of the critical policy extensions.
762         *
763         * @return The names of the critical policy extensions or if parsing
764         *         failed.
765         */
766        public List<String> getCriticalPolicyExtensions() {
767                
768                return getStringListClaim(POLICY_LANGUAGE_CRITICAL_CLAIM_NAME);
769        }
770        
771        
772        /**
773         * Sets the names of the critical policy extensions.
774         *
775         * @param extNames The names of the critical policy extensions,
776         *                 {@code null} if not specified. Must not be an empty
777         *                 list.
778         */
779        public void setCriticalPolicyExtensions(final List<String> extNames) {
780        
781                if (extNames != null && extNames.isEmpty()) {
782                        throw new IllegalArgumentException("The critical policy extension names must not be empty");
783                }
784                
785                setClaim(POLICY_LANGUAGE_CRITICAL_CLAIM_NAME, extNames);
786        }
787}