001/* 002* The contents of this file are subject to the terms of the Common Development and 003* Distribution License (the License). You may not use this file except in compliance with the 004* License. 005* 006* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007* specific language governing permission and limitations under the License. 008* 009* When distributing Covered Software, include this CDDL Header Notice in each file and include 010* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011* Header, with the fields enclosed by brackets [] replaced by your own identifying 012* information: "Portions copyright [year] [name of copyright owner]". 013* 014* Copyright 2014-2017 ForgeRock AS. 015*/ 016 017package org.forgerock.json.jose.jwk; 018 019import java.io.IOException; 020import java.net.URL; 021import java.security.Key; 022import java.util.HashMap; 023import java.util.Map; 024 025import org.forgerock.json.jose.exceptions.FailedToLoadJWKException; 026import org.forgerock.util.SimpleHTTPClient; 027 028/** 029 * Provides methods to gather a JWKSet from a URL and return 030 * a map of key ids to keys as dictated by that JWKS. 031 */ 032public class JWKSetParser { 033 034 private SimpleHTTPClient simpleHTTPClient; 035 private JWKLookup jwkLookup; 036 037 /** 038 * Constructor allowing the configuration of the read and connection timeouts used 039 * by the HTTP client for this parser. 040 * 041 * @param readTimeout read timeout in ms 042 * @param connTimeout connection timeout in ms 043 */ 044 public JWKSetParser(final int readTimeout, final int connTimeout) { 045 this(new SimpleHTTPClient(readTimeout, connTimeout)); 046 } 047 048 /** 049 * Alternative constructor allowing the calling class to pass in an 050 * already-configured {@link SimpleHTTPClient}. 051 * 052 * @param simpleHTTPClient {@link SimpleHTTPClient} used to gather HTTP information 053 */ 054 public JWKSetParser(final SimpleHTTPClient simpleHTTPClient) { 055 this(simpleHTTPClient, new JWKLookup()); 056 } 057 058 /** 059 * Alternative constructor allowing the calling class to pass in an 060 * already-configured {@link SimpleHTTPClient}. 061 * 062 * @param simpleHTTPClient {@link SimpleHTTPClient} used to gather HTTP information 063 * @param jwkLookup to convert the jwk into a real key 064 */ 065 public JWKSetParser(final SimpleHTTPClient simpleHTTPClient, final JWKLookup jwkLookup) { 066 this.simpleHTTPClient = simpleHTTPClient; 067 this.jwkLookup = jwkLookup; 068 } 069 070 /** 071 * Provides a Map of KeyId:Keys as indicated by the JWKSet's URL. 072 * 073 * @param url The URL from which to gather the JWKSet 074 * @return a map of currently valid KeyId:Keys for the provider associated with this URL 075 * @throws FailedToLoadJWKException If there are problems connecting to or parsing the response 076 */ 077 public Map<String, Key> generateMapFromJWK(URL url) throws FailedToLoadJWKException { 078 return jwkSetToMap(jwkSet(url)); 079 } 080 081 /** 082 * Uses the SimpleHTTPClient to gather HTTP information. 083 * 084 * @param url The URL from which to read the information 085 * @return a String containing the returned JSON 086 * @throws FailedToLoadJWKException If there are problems connecting to the URL 087 */ 088 private String gatherHttpContents(URL url) throws FailedToLoadJWKException { 089 try { 090 return simpleHTTPClient.get(url); 091 } catch (IOException e) { 092 throw new FailedToLoadJWKException("Unable to load the JWK location over HTTP", e); 093 } 094 } 095 096 /** 097 * Provides a jwks set as indicated by the JWKSet's URL. 098 * 099 * @param url The URL from which to gather the JWKSet 100 * @return a jwks set valid for the provider associated with this URL 101 * @throws FailedToLoadJWKException If there are problems connecting to or parsing the response 102 */ 103 public JWKSet jwkSet(URL url) throws FailedToLoadJWKException { 104 final String jwksContents = gatherHttpContents(url); 105 return JWKSet.parse(jwksContents); 106 } 107 108 /** 109 * Converts a supplied JWKSet into a map of key:values, where the keys are the keyIds and the 110 * values are verification keys. 111 * 112 * @param jwkSet The JWKSet to convert 113 * @return A map of key ids to their respective keys 114 * @throws FailedToLoadJWKException If there are issues parsing the JWKSet's contents 115 */ 116 public Map<String, Key> jwkSetToMap(JWKSet jwkSet) throws FailedToLoadJWKException { 117 118 final Map<String, Key> keyMap = new HashMap<>(); 119 120 //store the retrieved JSON as String (kid) : Key (having converted) in this resolver 121 for (JWK jwk : jwkSet.getJWKsAsList()) { 122 final Key key = jwkLookup.lookup(jwk.toJsonString(), jwk.getKeyType()); 123 keyMap.put(jwk.getKeyId(), key); 124 } 125 return keyMap; 126 } 127}