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.api;
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 * Resolve statement.
039 *
040 * <p>Related specifications:
041 *
042 * <ul>
043 *     <li>OpenID Connect Federation 1.0, section 7.2.2.
044 * </ul>
045 */
046@Immutable
047public final class ResolveStatement {
048        
049        
050        /**
051         * The resolve statement JOSE object type
052         * ({@code resolve-response+jwt}).
053         */
054        public static final JOSEObjectType JOSE_OBJECT_TYPE = new JOSEObjectType("resolve-response+jwt");
055        
056        
057        /**
058         * The resolve response content type
059         * ({@code application/resolve-response+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 ResolveClaimsSet claimsSet;
074        
075        
076        /**
077         * Creates a new resolve 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 ResolveStatement(final SignedJWT statementJWT,
084                                 final ResolveClaimsSet 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 signed statement.
103         *
104         * @return The signed statement as signed JWT.
105         */
106        public SignedJWT getSignedStatement() {
107                return statementJWT;
108        }
109        
110        
111        /**
112         * Returns the statement claims.
113         *
114         * @return The statement claims.
115         */
116        public ResolveClaimsSet getClaimsSet() {
117                return claimsSet;
118        }
119        
120        
121        /**
122         * Verifies the signature and checks the statement type, issue and
123         * expiration times.
124         *
125         * @param jwkSet The JWK set to use for the signature verification.
126         *               Must not be {@code null}.
127         *
128         * @return The SHA-256 thumbprint of the key used to successfully
129         *         verify the signature.
130         *
131         * @throws BadJOSEException If the signature is invalid or the
132         *                          statement is expired or before the issue
133         *                          time.
134         * @throws JOSEException    On an internal JOSE exception.
135         */
136        public Base64URL verifySignature(final JWKSet jwkSet)
137                throws BadJOSEException, JOSEException {
138                
139                return JWTUtils.verifySignature(
140                        statementJWT,
141                        JOSE_OBJECT_TYPE,
142                        new ResolveClaimsVerifier(),
143                        jwkSet);
144        }
145        
146        
147        /**
148         * Signs the specified resolve claims set.
149         *
150         * @param claimsSet  The claims set. Must not be {@code null}.
151         * @param signingJWK The private signing JWK. Must be contained in the
152         *                   entity JWK set and not {@code null}.
153         *
154         * @return The signed resolve statement.
155         *
156         * @throws JOSEException On a internal signing exception.
157         */
158        public static ResolveStatement sign(final ResolveClaimsSet claimsSet,
159                                            final JWK signingJWK)
160                throws JOSEException {
161                
162                return sign(claimsSet, signingJWK, JWTUtils.resolveSigningAlgorithm(signingJWK));
163        }
164        
165        
166        /**
167         * Signs the specified resolve claims set.
168         *
169         * @param claimsSet  The claims set. Must not be {@code null}.
170         * @param signingJWK The private signing JWK. Must be contained in the
171         *                   entity JWK set and not {@code null}.
172         * @param jwsAlg     The signing algorithm. Must be supported by the
173         *                   JWK and not {@code null}.
174         *
175         * @return The signed resolve statement.
176         *
177         * @throws JOSEException On an internal signing exception.
178         */
179        public static ResolveStatement sign(final ResolveClaimsSet claimsSet,
180                                            final JWK signingJWK,
181                                            final JWSAlgorithm jwsAlg)
182                throws JOSEException {
183                
184                try {
185                        return new ResolveStatement(
186                                JWTUtils.sign(
187                                        signingJWK,
188                                        jwsAlg,
189                                        JOSE_OBJECT_TYPE,
190                                        claimsSet.toJWTClaimsSet()),
191                                claimsSet);
192                } catch (ParseException e) {
193                        throw new JOSEException(e.getMessage(), e);
194                }
195        }
196        
197        
198        /**
199         * Parses a resolve statement.
200         *
201         * @param signedStmt The signed statement as a signed JWT. Must not be
202         *                   {@code null}.
203         *
204         * @return The resolve statement.
205         *
206         * @throws ParseException If parsing failed.
207         */
208        public static ResolveStatement parse(final SignedJWT signedStmt)
209                throws ParseException {
210                
211                return new ResolveStatement(signedStmt, new ResolveClaimsSet(JWTUtils.parseSignedJWTClaimsSet(signedStmt)));
212        }
213        
214        
215        /**
216         * Parses a resolve statement.
217         *
218         * @param signedStmtString The signed statement as a signed JWT string.
219         *                         Must not be {@code null}.
220         *
221         * @return The resolve statement.
222         *
223         * @throws ParseException If parsing failed.
224         */
225        public static ResolveStatement parse(final String signedStmtString)
226                throws ParseException {
227                
228                try {
229                        return parse(SignedJWT.parse(signedStmtString));
230                } catch (java.text.ParseException e) {
231                        throw new ParseException("Invalid resolve statement: " + e.getMessage(), e);
232                }
233        }
234}