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; 033 034 035/** 036 * DPoP utilities. 037 */ 038public final class DPoPUtils { 039 040 041 /** 042 * Creates a new DPoP JWT claims set. 043 * 044 * @param jti The JWT ID. Must not be {@code null}. 045 * @param htm The HTTP request method. Must not be 046 * {@code null}. 047 * @param htu The HTTP URI, without a query or fragment. Must 048 * not be {@code null}. 049 * @param iat The issue time. Must not be {@code null}. 050 * @param accessToken The access token for the access token hash 051 * ("ath") claim computation, {@code null} if not 052 * specified. 053 * 054 * @return The JWT claims set. 055 * 056 * @throws JOSEException If a cryptographic exception was encountered. 057 */ 058 public static JWTClaimsSet createJWTClaimsSet(final JWTID jti, 059 final String htm, 060 final URI htu, 061 final Date iat, 062 final AccessToken accessToken) 063 throws JOSEException { 064 065 if (StringUtils.isBlank(htm)) { 066 throw new IllegalArgumentException("The HTTP method (htu) is required"); 067 } 068 069 if (htu.getQuery() != null) { 070 throw new IllegalArgumentException("The HTTP URI (htu) must not have a query"); 071 } 072 073 if (htu.getFragment() != null) { 074 throw new IllegalArgumentException("The HTTP URI (htu) must not have a fragment"); 075 } 076 077 if (iat == null) { 078 throw new IllegalArgumentException("The issue time (iat) is required"); 079 } 080 081 JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder() 082 .jwtID(jti.getValue()) 083 .claim("htm", htm) 084 .claim("htu", htu.toString()) 085 .issueTime(iat); 086 087 if (accessToken != null) { 088 builder = builder.claim("ath", computeSHA256(accessToken).toString()); 089 } 090 091 return builder.build(); 092 } 093 094 095 /** 096 * Computes a SHA-256 hash for the specified access token. 097 * 098 * @param accessToken The access token. Must not be {@code null}. 099 * 100 * @return The hash, BASE64 URL encoded. 101 * 102 * @throws JOSEException If hashing failed. 103 */ 104 public static Base64URL computeSHA256(final AccessToken accessToken) 105 throws JOSEException { 106 107 byte[] hash; 108 try { 109 MessageDigest md = MessageDigest.getInstance("SHA-256"); 110 hash = md.digest(accessToken.getValue().getBytes(StandardCharsets.UTF_8)); 111 } catch (NoSuchAlgorithmException e) { 112 throw new JOSEException(e.getMessage(), e); 113 } 114 115 return Base64URL.encode(hash); 116 } 117 118 119 /** 120 *Prevents public instantiation. 121 */ 122 private DPoPUtils() {} 123}