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.oauth2.sdk.dpop;
019
020
021import java.net.URI;
022import java.nio.charset.StandardCharsets;
023import java.security.MessageDigest;
024import java.security.NoSuchAlgorithmException;
025import java.util.Date;
026
027import com.nimbusds.jose.JOSEException;
028import com.nimbusds.jose.util.Base64URL;
029import com.nimbusds.jwt.JWTClaimsSet;
030import com.nimbusds.oauth2.sdk.id.JWTID;
031import com.nimbusds.oauth2.sdk.token.AccessToken;
032import com.nimbusds.oauth2.sdk.util.StringUtils;
033import com.nimbusds.openid.connect.sdk.Nonce;
034
035
036/**
037 * DPoP utilities.
038 */
039public final class DPoPUtils {
040        
041        
042        /**
043         * Creates a new DPoP JWT claims set.
044         *
045         * @param jti         The JWT ID. Must not be {@code null}.
046         * @param htm         The HTTP request method. Must not be
047         *                    {@code null}.
048         * @param htu         The HTTP URI, without a query or fragment. Must
049         *                    not be {@code null}.
050         * @param iat         The issue time. Must not be {@code null}.
051         * @param accessToken The access token for the access token hash
052         *                    ("ath") claim computation, {@code null} if not
053         *                    specified.
054         *
055         * @return The JWT claims set.
056         *
057         * @throws JOSEException If a cryptographic exception was encountered.
058         */
059        @Deprecated
060        public static JWTClaimsSet createJWTClaimsSet(final JWTID jti,
061                                                      final String htm,
062                                                      final URI htu,
063                                                      final Date iat,
064                                                      final AccessToken accessToken)
065                throws JOSEException {
066                
067                return createJWTClaimsSet(jti, htm, htu, iat, accessToken, null);
068        }
069        
070        
071        /**
072         * Creates a new DPoP JWT claims set.
073         *
074         * @param jti         The JWT ID. Must not be {@code null}.
075         * @param htm         The HTTP request method. Must not be
076         *                    {@code null}.
077         * @param htu         The HTTP URI, without a query or fragment. Must
078         *                    not be {@code null}.
079         * @param iat         The issue time. Must not be {@code null}.
080         * @param accessToken The access token for the access token hash
081         *                    ("ath") claim computation, {@code null} if not
082         *                    specified.
083         * @param nonce       The nonce, {@code null} if not specified.
084         *
085         * @return The JWT claims set.
086         *
087         * @throws JOSEException If a cryptographic exception was encountered.
088         */
089        public static JWTClaimsSet createJWTClaimsSet(final JWTID jti,
090                                                      final String htm,
091                                                      final URI htu,
092                                                      final Date iat,
093                                                      final AccessToken accessToken,
094                                                      final Nonce nonce)
095                throws JOSEException {
096                
097                if (StringUtils.isBlank(htm)) {
098                        throw new IllegalArgumentException("The HTTP method (htu) is required");
099                }
100                
101                if (htu.getQuery() != null) {
102                        throw new IllegalArgumentException("The HTTP URI (htu) must not have a query");
103                }
104                
105                if (htu.getFragment() != null) {
106                        throw new IllegalArgumentException("The HTTP URI (htu) must not have a fragment");
107                }
108                
109                if (iat == null) {
110                        throw new IllegalArgumentException("The issue time (iat) is required");
111                }
112                
113                JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder()
114                        .jwtID(jti.getValue())
115                        .claim("htm", htm)
116                        .claim("htu", htu.toString())
117                        .issueTime(iat);
118                
119                if (accessToken != null) {
120                        builder = builder.claim("ath", computeSHA256(accessToken).toString());
121                }
122                
123                if (nonce != null) {
124                        builder = builder.claim("nonce", nonce.getValue());
125                }
126                
127                return builder.build();
128        }
129        
130        
131        /**
132         * Computes a SHA-256 hash for the specified access token.
133         *
134         * @param accessToken The access token. Must not be {@code null}.
135         *
136         * @return The hash, BASE64 URL encoded.
137         *
138         * @throws JOSEException If hashing failed.
139         */
140        public static Base64URL computeSHA256(final AccessToken accessToken)
141                throws JOSEException {
142                
143                byte[] hash;
144                try {
145                        MessageDigest md = MessageDigest.getInstance("SHA-256");
146                        hash = md.digest(accessToken.getValue().getBytes(StandardCharsets.UTF_8));
147                } catch (NoSuchAlgorithmException e) {
148                        throw new JOSEException(e.getMessage(), e);
149                }
150                
151                return Base64URL.encode(hash);
152        }
153        
154        
155      /**
156       *Prevents public instantiation.
157       */
158      private DPoPUtils() {}
159}