001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2020, 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 net.jcip.annotations.Immutable; 022 023import com.nimbusds.common.contenttype.ContentType; 024import com.nimbusds.jose.JOSEException; 025import com.nimbusds.jose.JOSEObjectType; 026import com.nimbusds.jose.JWSAlgorithm; 027import com.nimbusds.jose.JWSObject; 028import com.nimbusds.jose.jwk.JWK; 029import com.nimbusds.jose.jwk.JWKSet; 030import com.nimbusds.jose.proc.BadJOSEException; 031import com.nimbusds.jose.util.Base64URL; 032import com.nimbusds.jwt.SignedJWT; 033import com.nimbusds.oauth2.sdk.ParseException; 034import com.nimbusds.openid.connect.sdk.federation.utils.JWTUtils; 035 036 037/** 038 * Federation entity statement / entity configuration. 039 * 040 * <p>Related specifications: 041 * 042 * <ul> 043 * <li>OpenID Connect Federation 1.0, section 3.1. 044 * </ul> 045 */ 046@Immutable 047public final class EntityStatement { 048 049 050 /** 051 * The federation entity statement JOSE object type 052 * ({@code entity-statement+jwt}). 053 */ 054 public static final JOSEObjectType JOSE_OBJECT_TYPE = new JOSEObjectType("entity-statement+jwt"); 055 056 057 /** 058 * The federation entity statement content type 059 * ({@code application/entity-statement+jwt}). 060 */ 061 public static final ContentType CONTENT_TYPE = new ContentType("application", JOSE_OBJECT_TYPE.getType()); 062 063 064 /** 065 * The signed statement as signed JWT. 066 */ 067 private final SignedJWT statementJWT; 068 069 070 /** 071 * The statement claims. 072 */ 073 private final EntityStatementClaimsSet claimsSet; 074 075 076 /** 077 * Creates a new federation entity statement. 078 * 079 * @param statementJWT The signed statement as signed JWT. Must not be 080 * {@code null}. 081 * @param claimsSet The statement claims. Must not be {@code null}. 082 */ 083 private EntityStatement(final SignedJWT statementJWT, 084 final EntityStatementClaimsSet claimsSet) { 085 086 if (statementJWT == null) { 087 throw new IllegalArgumentException("The entity statement must not be null"); 088 } 089 if (JWSObject.State.UNSIGNED.equals(statementJWT.getState())) { 090 throw new IllegalArgumentException("The statement is not signed"); 091 } 092 this.statementJWT = statementJWT; 093 094 if (claimsSet == null) { 095 throw new IllegalArgumentException("The entity statement claims set must not be null"); 096 } 097 this.claimsSet = claimsSet; 098 } 099 100 101 /** 102 * Returns the entity ID. 103 * 104 * @return The entity ID. 105 */ 106 public EntityID getEntityID() { 107 return getClaimsSet().getSubjectEntityID(); 108 } 109 110 111 /** 112 * Returns the signed statement. 113 * 114 * @return The signed statement as signed JWT. 115 */ 116 public SignedJWT getSignedStatement() { 117 return statementJWT; 118 } 119 120 121 /** 122 * Returns the statement claims. 123 * 124 * @return The statement claims. 125 */ 126 public EntityStatementClaimsSet getClaimsSet() { 127 return claimsSet; 128 } 129 130 131 /** 132 * Verifies the signature for a self-statement (typically for a trust 133 * anchor or leaf) and checks the statement issue and expiration times. 134 * 135 * @return The SHA-256 thumbprint of the key used to successfully 136 * verify the signature. 137 * 138 * @throws BadJOSEException If the signature is invalid or the 139 * statement is expired or before the issue 140 * time. 141 * @throws JOSEException On a internal JOSE exception. 142 */ 143 public Base64URL verifySignatureOfSelfStatement() throws BadJOSEException, JOSEException { 144 145 if (! getClaimsSet().isSelfStatement()) { 146 throw new BadJOSEException("Entity statement not self-issued"); 147 } 148 149 return verifySignature(getClaimsSet().getJWKSet()); 150 } 151 152 153 /** 154 * Verifies the signature and checks the statement type, issue and 155 * expiration times. 156 * 157 * @param jwkSet The JWK set to use for the signature verification. 158 * Must not be {@code null}. 159 * 160 * @return The SHA-256 thumbprint of the key used to successfully 161 * verify the signature. 162 * 163 * @throws BadJOSEException If the signature is invalid or the 164 * statement is expired or before the issue 165 * time. 166 * @throws JOSEException On an internal JOSE exception. 167 */ 168 public Base64URL verifySignature(final JWKSet jwkSet) 169 throws BadJOSEException, JOSEException { 170 171 return JWTUtils.verifySignature( 172 statementJWT, 173 JOSE_OBJECT_TYPE, 174 new EntityStatementClaimsVerifier(null), 175 jwkSet); 176 } 177 178 179 /** 180 * Signs the specified federation entity claims set. 181 * 182 * @param claimsSet The claims set. Must not be {@code null}. 183 * @param signingJWK The private signing JWK. Must be contained in the 184 * entity JWK set and not {@code null}. 185 * 186 * @return The signed federation entity statement. 187 * 188 * @throws JOSEException On a internal signing exception. 189 */ 190 public static EntityStatement sign(final EntityStatementClaimsSet claimsSet, 191 final JWK signingJWK) 192 throws JOSEException { 193 194 return sign(claimsSet, signingJWK, JWTUtils.resolveSigningAlgorithm(signingJWK)); 195 } 196 197 198 /** 199 * Signs the specified federation entity claims set. 200 * 201 * @param claimsSet The claims set. Must not be {@code null}. 202 * @param signingJWK The private signing JWK. Must be contained in the 203 * entity JWK set and not {@code null}. 204 * @param jwsAlg The signing algorithm. Must be supported by the 205 * JWK and not {@code null}. 206 * 207 * @return The signed federation entity statement. 208 * 209 * @throws JOSEException On a internal signing exception. 210 */ 211 public static EntityStatement sign(final EntityStatementClaimsSet claimsSet, 212 final JWK signingJWK, 213 final JWSAlgorithm jwsAlg) 214 throws JOSEException { 215 216 if (claimsSet.isSelfStatement() && ! claimsSet.getJWKSet().containsJWK(signingJWK)) { 217 throw new JOSEException("Signing JWK not found in JWK set of self-statement"); 218 } 219 220 try { 221 return new EntityStatement( 222 JWTUtils.sign( 223 signingJWK, 224 jwsAlg, 225 JOSE_OBJECT_TYPE, 226 claimsSet.toJWTClaimsSet()), 227 claimsSet); 228 } catch (ParseException e) { 229 throw new JOSEException(e.getMessage(), e); 230 } 231 } 232 233 234 /** 235 * Parses a federation entity statement. 236 * 237 * @param signedStmt The signed statement as a signed JWT. Must not be 238 * {@code null}. 239 * 240 * @return The federation entity statement. 241 * 242 * @throws ParseException If parsing failed. 243 */ 244 public static EntityStatement parse(final SignedJWT signedStmt) 245 throws ParseException { 246 247 return new EntityStatement(signedStmt, new EntityStatementClaimsSet(JWTUtils.parseSignedJWTClaimsSet(signedStmt))); 248 } 249 250 251 /** 252 * Parses a federation entity statement. 253 * 254 * @param signedStmtString The signed statement as a signed JWT string. 255 * Must not be {@code null}. 256 * 257 * @return The federation entity statement. 258 * 259 * @throws ParseException If parsing failed. 260 */ 261 public static EntityStatement parse(final String signedStmtString) 262 throws ParseException { 263 264 try { 265 return parse(SignedJWT.parse(signedStmtString)); 266 } catch (java.text.ParseException e) { 267 throw new ParseException("Invalid entity statement: " + e.getMessage(), e); 268 } 269 } 270}