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-2015 ForgeRock AS. 015*/ 016 017package org.forgerock.jaspi.modules.openid.helpers; 018 019import java.io.IOException; 020import java.net.URL; 021import java.security.Key; 022import java.util.HashMap; 023import java.util.Map; 024import org.forgerock.jaspi.modules.openid.exceptions.FailedToLoadJWKException; 025import org.forgerock.json.jose.jwk.JWK; 026import org.forgerock.json.jose.jwk.JWKSet; 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 //gather 079 final String jwksContents = gatherHttpContents(url); 080 081 //unmarshall 082 final JWKSet jwkSet = JWKSet.parse(jwksContents); 083 084 //return map 085 return jwkSetToMap(jwkSet); 086 } 087 088 /** 089 * Uses the SimpleHTTPClient to gather HTTP information. 090 * 091 * @param url The URL from which to read the information 092 * @return a String containing the returned JSON 093 * @throws FailedToLoadJWKException If there are problems connecting to the URL 094 */ 095 private String gatherHttpContents(URL url) throws FailedToLoadJWKException { 096 final String jwksContents; 097 098 try { 099 jwksContents = simpleHTTPClient.get(url); 100 } catch (IOException e) { 101 throw new FailedToLoadJWKException("Unable to load the JWK location over HTTP", e); 102 } 103 104 return jwksContents; 105 } 106 107 /** 108 * Converts a supplied JWKSet into a map of key:values, where the keys are the keyIds and the 109 * values are verification keys. 110 * 111 * @param jwkSet The JWKSet to convert 112 * @return A map of key ids to their respective keys 113 * @throws FailedToLoadJWKException If there are issues parsing the JWKSet's contents 114 */ 115 public Map<String, Key> jwkSetToMap(JWKSet jwkSet) throws FailedToLoadJWKException { 116 117 final Map<String, Key> keyMap = new HashMap<>(); 118 119 //store the retrieved JSON as String (kid) : Key (having converted) in this resolver 120 for (JWK jwk : jwkSet.getJWKsAsList()) { 121 final Key key = jwkLookup.lookup(jwk.toJsonString(), jwk.getKeyType()); 122 keyMap.put(jwk.getKeyId(), key); 123 } 124 125 return keyMap; 126 127 } 128 129}