001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, 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.jose.jwk; 019 020 021import java.net.URI; 022import java.net.URISyntaxException; 023import java.text.ParseException; 024import java.util.Objects; 025 026import net.jcip.annotations.Immutable; 027 028import com.nimbusds.jose.JOSEException; 029import com.nimbusds.jose.util.Base64URL; 030 031 032/** 033 * JSON Web Key (JWK) thumbprint URI. 034 * 035 * <p>Example SHA-256 thumbprint URI: 036 * 037 * <pre> 038 * urn:ietf:params:oauth:jwk-thumbprint:sha-256:NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs 039 * </pre> 040 * 041 * <p>See draft-ietf-oauth-jwk-thumbprint-uri-01 042 * 043 * @author Vladimir Dzhuvinov 044 * @version 2024-04-20 045 */ 046@Immutable 047public class ThumbprintURI { 048 049 050 /** 051 * The URI prefix of JWK thumbprints. 052 */ 053 public static final String PREFIX = "urn:ietf:params:oauth:jwk-thumbprint:"; 054 055 056 /** 057 * The hash algorithm. 058 */ 059 private final String hashAlg; 060 061 062 /** 063 * The thumbprint value. 064 */ 065 private final Base64URL thumbprint; 066 067 068 /** 069 * Creates a new JWK thumbprint URI. 070 * 071 * @param hashAlg The hash algorithm. Must not be {@code null}. 072 * @param thumbprint The thumbprint value. Must not be {@code null}. 073 */ 074 public ThumbprintURI(final String hashAlg, final Base64URL thumbprint) { 075 if (hashAlg.isEmpty()) { 076 throw new IllegalArgumentException("The hash algorithm must not be empty"); 077 } 078 this.hashAlg = hashAlg; 079 080 if (thumbprint.toString().isEmpty()) { 081 throw new IllegalArgumentException("The thumbprint must not be empty"); 082 } 083 this.thumbprint = thumbprint; 084 } 085 086 087 /** 088 * Returns the hash algorithm string. 089 * 090 * @return The hash algorithm string. 091 */ 092 public String getAlgorithmString() { 093 094 return hashAlg; 095 } 096 097 098 /** 099 * Returns the underlying thumbprint value. 100 * 101 * @return The thumbprint value. 102 */ 103 public Base64URL getThumbprint() { 104 105 return thumbprint; 106 } 107 108 109 /** 110 * Returns the {@link URI} representation. 111 * 112 * @return The {@link URI} representation. 113 */ 114 public URI toURI() { 115 116 return URI.create(toString()); 117 } 118 119 120 @Override 121 public String toString() { 122 123 return PREFIX + hashAlg + ":" + thumbprint; 124 } 125 126 127 @Override 128 public boolean equals(Object o) { 129 if (this == o) return true; 130 if (!(o instanceof ThumbprintURI)) return false; 131 ThumbprintURI that = (ThumbprintURI) o; 132 return hashAlg.equals(that.hashAlg) && getThumbprint().equals(that.getThumbprint()); 133 } 134 135 136 @Override 137 public int hashCode() { 138 return Objects.hash(hashAlg, getThumbprint()); 139 } 140 141 142 /** 143 * Computes the SHA-256 JWK thumbprint URI for the specified JWK. 144 * 145 * @param jwk The JWK. Must not be {@code null}. 146 * 147 * @return The SHA-256 JWK thumbprint URI. 148 * 149 * @throws JOSEException If the SHA-256 hash algorithm is not 150 * supported. 151 */ 152 public static ThumbprintURI compute(final JWK jwk) 153 throws JOSEException { 154 155 return new ThumbprintURI("sha-256", jwk.computeThumbprint()); 156 } 157 158 159 /** 160 * Parses a JWK thumbprint URI from the specified URI. 161 * 162 * @param uri The URI. Must not be {@code null}. 163 * 164 * @return The JWK thumbprint URI. 165 * 166 * @throws ParseException If the URI is illegal. 167 */ 168 public static ThumbprintURI parse(final URI uri) 169 throws ParseException { 170 171 String uriString = uri.toString(); 172 if (! uriString.startsWith(PREFIX)) { 173 throw new ParseException("Illegal JWK thumbprint prefix", 0); 174 } 175 176 String valuesString = uriString.substring(PREFIX.length()); 177 if (valuesString.isEmpty()) { 178 throw new ParseException("Illegal JWK thumbprint: Missing value", 0); 179 } 180 181 String[] values = valuesString.split(":"); 182 if (values.length != 2) { 183 throw new ParseException("Illegal JWK thumbprint: Unexpected number of components", 0); 184 } 185 if (values[0].isEmpty()) { 186 throw new ParseException("Illegal JWK thumbprint: The hash algorithm must not be empty", 0); 187 } 188 // Empty thumbprint prevented by split method 189 190 return new ThumbprintURI(values[0], new Base64URL(values[1])); 191 } 192 193 194 /** 195 * Parses a JWK thumbprint URI from the specified URI string. 196 * 197 * @param s The URI string. Must not be {@code null}. 198 * 199 * @return The JWK thumbprint URI. 200 * 201 * @throws ParseException If the URI string is illegal. 202 */ 203 public static ThumbprintURI parse(final String s) 204 throws ParseException { 205 206 try { 207 return parse(new URI(s)); 208 } catch (URISyntaxException e) { 209 throw new ParseException(e.getMessage(), 0); 210 } 211 } 212}