001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 003 * 004 * Portions copyright 2011-2013 The MITRE Corporation 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the 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 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 *******************************************************************************/ 018/** 019 * 020 */ 021package org.mitre.jwt.signer.service.impl; 022 023import java.util.concurrent.ExecutionException; 024import java.util.concurrent.TimeUnit; 025 026import org.apache.http.client.HttpClient; 027import org.apache.http.impl.client.HttpClientBuilder; 028import org.mitre.jose.keystore.JWKSetKeyStore; 029import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService; 030import org.mitre.jwt.encryption.service.impl.DefaultJWTEncryptionAndDecryptionService; 031import org.mitre.jwt.signer.service.JWTSigningAndValidationService; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; 035import org.springframework.stereotype.Service; 036import org.springframework.web.client.RestClientException; 037import org.springframework.web.client.RestTemplate; 038 039import com.google.common.cache.CacheBuilder; 040import com.google.common.cache.CacheLoader; 041import com.google.common.cache.LoadingCache; 042import com.google.common.util.concurrent.UncheckedExecutionException; 043import com.google.gson.JsonParseException; 044import com.nimbusds.jose.jwk.JWKSet; 045 046/** 047 * 048 * Creates a caching map of JOSE signers/validators and encrypters/decryptors 049 * keyed on the JWK Set URI. Dynamically loads JWK Sets to create the services. 050 * 051 * @author jricher 052 * 053 */ 054@Service 055public class JWKSetCacheService { 056 057 /** 058 * Logger for this class 059 */ 060 private static final Logger logger = LoggerFactory.getLogger(JWKSetCacheService.class); 061 062 // map of jwk set uri -> signing/validation service built on the keys found in that jwk set 063 private LoadingCache<String, JWTSigningAndValidationService> validators; 064 065 // map of jwk set uri -> encryption/decryption service built on the keys found in that jwk set 066 private LoadingCache<String, JWTEncryptionAndDecryptionService> encrypters; 067 068 public JWKSetCacheService() { 069 this.validators = CacheBuilder.newBuilder() 070 .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch 071 .maximumSize(100) 072 .build(new JWKSetVerifierFetcher(HttpClientBuilder.create().useSystemProperties().build())); 073 this.encrypters = CacheBuilder.newBuilder() 074 .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch 075 .maximumSize(100) 076 .build(new JWKSetEncryptorFetcher(HttpClientBuilder.create().useSystemProperties().build())); 077 } 078 079 /** 080 * @param jwksUri 081 * @return 082 * @throws ExecutionException 083 * @see com.google.common.cache.Cache#get(java.lang.Object) 084 */ 085 public JWTSigningAndValidationService getValidator(String jwksUri) { 086 try { 087 return validators.get(jwksUri); 088 } catch (UncheckedExecutionException | ExecutionException e) { 089 logger.warn("Couldn't load JWK Set from " + jwksUri + ": " + e.getMessage()); 090 return null; 091 } 092 } 093 094 public JWTEncryptionAndDecryptionService getEncrypter(String jwksUri) { 095 try { 096 return encrypters.get(jwksUri); 097 } catch (UncheckedExecutionException | ExecutionException e) { 098 logger.warn("Couldn't load JWK Set from " + jwksUri + ": " + e.getMessage()); 099 return null; 100 } 101 } 102 103 /** 104 * @author jricher 105 * 106 */ 107 private class JWKSetVerifierFetcher extends CacheLoader<String, JWTSigningAndValidationService> { 108 private HttpComponentsClientHttpRequestFactory httpFactory; 109 private RestTemplate restTemplate; 110 111 JWKSetVerifierFetcher(HttpClient httpClient) { 112 this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); 113 this.restTemplate = new RestTemplate(httpFactory); 114 } 115 116 /** 117 * Load the JWK Set and build the appropriate signing service. 118 */ 119 @Override 120 public JWTSigningAndValidationService load(String key) throws Exception { 121 String jsonString = restTemplate.getForObject(key, String.class); 122 JWKSet jwkSet = JWKSet.parse(jsonString); 123 124 JWKSetKeyStore keyStore = new JWKSetKeyStore(jwkSet); 125 126 JWTSigningAndValidationService service = new DefaultJWTSigningAndValidationService(keyStore); 127 128 return service; 129 } 130 131 } 132 133 /** 134 * @author jricher 135 * 136 */ 137 private class JWKSetEncryptorFetcher extends CacheLoader<String, JWTEncryptionAndDecryptionService> { 138 private HttpComponentsClientHttpRequestFactory httpFactory; 139 private RestTemplate restTemplate; 140 141 public JWKSetEncryptorFetcher(HttpClient httpClient) { 142 this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); 143 this.restTemplate = new RestTemplate(httpFactory); 144 } 145 146 /* (non-Javadoc) 147 * @see com.google.common.cache.CacheLoader#load(java.lang.Object) 148 */ 149 @Override 150 public JWTEncryptionAndDecryptionService load(String key) throws Exception { 151 try { 152 String jsonString = restTemplate.getForObject(key, String.class); 153 JWKSet jwkSet = JWKSet.parse(jsonString); 154 155 JWKSetKeyStore keyStore = new JWKSetKeyStore(jwkSet); 156 157 JWTEncryptionAndDecryptionService service = new DefaultJWTEncryptionAndDecryptionService(keyStore); 158 159 return service; 160 } catch (JsonParseException | RestClientException e) { 161 throw new IllegalArgumentException("Unable to load JWK Set"); 162 } 163 } 164 } 165 166}