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}