001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2021, 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.dpop;
019
020
021import java.util.AbstractMap;
022import java.util.Map;
023import java.util.Objects;
024
025import net.jcip.annotations.Immutable;
026import net.minidev.json.JSONObject;
027
028import com.nimbusds.jose.JOSEException;
029import com.nimbusds.jose.jwk.JWK;
030import com.nimbusds.jose.util.Base64URL;
031import com.nimbusds.jwt.JWTClaimsSet;
032import com.nimbusds.oauth2.sdk.ParseException;
033import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
034
035
036/**
037 * JSON Web Key (JWK) SHA-256 thumbprint confirmation.
038 */
039@Immutable
040public final class JWKThumbprintConfirmation {
041        
042        
043        /**
044         * The JWK SHA-256 thumbprint.
045         */
046        private final Base64URL jkt;
047        
048        
049        /**
050         * Creates a new JWK SHA-256 thumbprint.
051         *
052         * @param jkt The JWK SHA-256 thumbprint. Must not be {@code null}.
053         */
054        public JWKThumbprintConfirmation(final Base64URL jkt) {
055                
056                if (jkt == null) {
057                        throw new IllegalArgumentException("The JWK thumbprint must not be null");
058                }
059                
060                this.jkt = jkt;
061        }
062        
063        
064        /**
065         * Returns the JWK SHA-256 thumbprint.
066         *
067         * @return The JWK SHA-256 thumbprint.
068         */
069        public Base64URL getValue() {
070                
071                return jkt;
072        }
073        
074        
075        /**
076         * Returns this JWK SHA-256 thumbprint confirmation as a JSON object.
077         *
078         * <p>Example:
079         *
080         * <pre>
081         * {
082         *   "cnf" : { "jkt" : "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" }
083         * }
084         * </pre>
085         *
086         * @return The JSON object.
087         */
088        public JSONObject toJSONObject() {
089                
090                JSONObject jsonObject = new JSONObject();
091                Map.Entry<String,JSONObject> cnfClaim = toJWTClaim();
092                jsonObject.put(cnfClaim.getKey(), cnfClaim.getValue());
093                return jsonObject;
094        }
095        
096        
097        /**
098         * Returns this JWK SHA-256 thumbprint confirmation as a JWT claim.
099         *
100         * <p>Example:
101         *
102         * <pre>
103         * "cnf" : { "jkt" : "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" }
104         * </pre>
105         *
106         * @return The JWT claim name / value.
107         */
108        public Map.Entry<String,JSONObject> toJWTClaim() {
109                
110                JSONObject cnf = new JSONObject();
111                cnf.put("jkt", jkt.toString());
112                
113                return new AbstractMap.SimpleImmutableEntry<>(
114                        "cnf",
115                        cnf
116                );
117        }
118        
119        
120        /**
121         * Applies this JWK SHA-256 thumbprint confirmation to the specified
122         * JWT claims set.
123         *
124         * @param jwtClaimsSet The JWT claims set.
125         *
126         * @return The modified JWT claims set.
127         */
128        public JWTClaimsSet applyTo(final JWTClaimsSet jwtClaimsSet) {
129                
130                Map.Entry<String,JSONObject> cnfClaim = toJWTClaim();
131                
132                return new JWTClaimsSet.Builder(jwtClaimsSet)
133                        .claim(cnfClaim.getKey(), cnfClaim.getValue())
134                        .build();
135        }
136        
137        
138        @Override
139        public String toString() {
140                return toJSONObject().toJSONString();
141        }
142        
143        
144        @Override
145        public boolean equals(Object o) {
146                if (this == o) return true;
147                if (!(o instanceof JWKThumbprintConfirmation)) return false;
148                JWKThumbprintConfirmation that = (JWKThumbprintConfirmation) o;
149                return jkt.equals(that.jkt);
150        }
151        
152        
153        @Override
154        public int hashCode() {
155                return Objects.hash(jkt);
156        }
157        
158        
159        /**
160         * Parses a JWK SHA-256 thumbprint confirmation from the specified JWT
161         * claims set.
162         *
163         * @param jwtClaimsSet The JWT claims set.
164         *
165         * @return The JWK SHA-256 thumbprint confirmation, {@code null} if not
166         *         found.
167         */
168        public static JWKThumbprintConfirmation parse(final JWTClaimsSet jwtClaimsSet) {
169                
170                Map<String, Object> jsonObjectClaim;
171                try {
172                        jsonObjectClaim = jwtClaimsSet.getJSONObjectClaim("cnf");
173                } catch (java.text.ParseException e) {
174                        return null;
175                }
176                
177                if (jsonObjectClaim == null) {
178                        return null;
179                }
180                
181                return parseFromConfirmationJSONObject(new JSONObject(jsonObjectClaim));
182        }
183        
184        
185        /**
186         * Parses a JWK SHA-256 thumbprint confirmation from the specified JSON
187         * object representation of a JWT claims set.
188         *
189         * @param jsonObject The JSON object.
190         *
191         * @return The JWK SHA-256 thumbprint confirmation, {@code null} if not
192         *         found.
193         */
194        public static JWKThumbprintConfirmation parse(final JSONObject jsonObject) {
195                
196                if (! jsonObject.containsKey("cnf")) {
197                        return null;
198                }
199                
200                try {
201                        return parseFromConfirmationJSONObject(JSONObjectUtils.getJSONObject(jsonObject, "cnf"));
202                } catch (ParseException e) {
203                        return null;
204                }
205        }
206        
207        
208        /**
209         * Parses a JWK SHA-256 thumbprint confirmation from the specified
210         * confirmation ("cnf") JSON object.
211         *
212         * @param cnf The confirmation JSON object, {@code null} if none.
213         *
214         * @return The JWK SHA-256 thumbprint confirmation, {@code null} if not
215         *         found.
216         */
217        public static JWKThumbprintConfirmation parseFromConfirmationJSONObject(final JSONObject cnf) {
218                
219                if (cnf == null) {
220                        return null;
221                }
222                
223                try {
224                        String jktString = JSONObjectUtils.getString(cnf, "jkt");
225                        return new JWKThumbprintConfirmation(new Base64URL(jktString));
226                } catch (ParseException e) {
227                        return null;
228                }
229        }
230        
231        
232        /**
233         * Creates a confirmation of the specified JWK.
234         *
235         * @param jwk The JWK.
236         *
237         * @return The JWK SHA-256 thumbprint confirmation.
238         *
239         * @throws JOSEException If the thumbprint computation failed.
240         */
241        public static JWKThumbprintConfirmation of(final JWK jwk)
242                throws JOSEException {
243                
244                return new JWKThumbprintConfirmation(jwk.computeThumbprint());
245        }
246}