001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 *******************************************************************************/ 016 017package org.mitre.jwt.signer.service.impl; 018 019import java.util.concurrent.ExecutionException; 020import java.util.concurrent.TimeUnit; 021 022import org.mitre.jose.keystore.JWKSetKeyStore; 023import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService; 024import org.mitre.jwt.encryption.service.impl.DefaultJWTEncryptionAndDecryptionService; 025import org.mitre.jwt.signer.service.JWTSigningAndValidationService; 026import org.mitre.oauth2.model.ClientDetailsEntity; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029import org.springframework.beans.factory.annotation.Autowired; 030import org.springframework.stereotype.Service; 031 032import com.google.common.base.Strings; 033import com.google.common.cache.CacheBuilder; 034import com.google.common.cache.CacheLoader; 035import com.google.common.cache.LoadingCache; 036import com.google.common.util.concurrent.UncheckedExecutionException; 037import com.nimbusds.jose.JWSAlgorithm; 038import com.nimbusds.jose.jwk.JWKSet; 039 040/** 041 * 042 * Takes in a client and returns the appropriate validator or encrypter for 043 * that client's registered key types. 044 * 045 * @author jricher 046 * 047 */ 048@Service 049public class ClientKeyCacheService { 050 051 private static Logger logger = LoggerFactory.getLogger(ClientKeyCacheService.class); 052 053 @Autowired 054 private JWKSetCacheService jwksUriCache = new JWKSetCacheService(); 055 056 @Autowired 057 private SymmetricKeyJWTValidatorCacheService symmetricCache = new SymmetricKeyJWTValidatorCacheService(); 058 059 // cache of validators for by-value JWKs 060 private LoadingCache<JWKSet, JWTSigningAndValidationService> jwksValidators; 061 062 // cache of encryptors for by-value JWKs 063 private LoadingCache<JWKSet, JWTEncryptionAndDecryptionService> jwksEncrypters; 064 065 public ClientKeyCacheService() { 066 this.jwksValidators = CacheBuilder.newBuilder() 067 .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch 068 .maximumSize(100) 069 .build(new JWKSetVerifierBuilder()); 070 this.jwksEncrypters = CacheBuilder.newBuilder() 071 .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch 072 .maximumSize(100) 073 .build(new JWKSetEncryptorBuilder()); 074 } 075 076 077 public JWTSigningAndValidationService getValidator(ClientDetailsEntity client, JWSAlgorithm alg) { 078 079 try { 080 if (alg.equals(JWSAlgorithm.RS256) 081 || alg.equals(JWSAlgorithm.RS384) 082 || alg.equals(JWSAlgorithm.RS512) 083 || alg.equals(JWSAlgorithm.ES256) 084 || alg.equals(JWSAlgorithm.ES384) 085 || alg.equals(JWSAlgorithm.ES512) 086 || alg.equals(JWSAlgorithm.PS256) 087 || alg.equals(JWSAlgorithm.PS384) 088 || alg.equals(JWSAlgorithm.PS512)) { 089 090 // asymmetric key 091 if (client.getJwks() != null) { 092 return jwksValidators.get(client.getJwks()); 093 } else if (!Strings.isNullOrEmpty(client.getJwksUri())) { 094 return jwksUriCache.getValidator(client.getJwksUri()); 095 } else { 096 return null; 097 } 098 099 } else if (alg.equals(JWSAlgorithm.HS256) 100 || alg.equals(JWSAlgorithm.HS384) 101 || alg.equals(JWSAlgorithm.HS512)) { 102 103 // symmetric key 104 105 return symmetricCache.getSymmetricValidtor(client); 106 107 } else { 108 109 return null; 110 } 111 } catch (UncheckedExecutionException | ExecutionException e) { 112 logger.error("Problem loading client validator", e); 113 return null; 114 } 115 116 } 117 118 public JWTEncryptionAndDecryptionService getEncrypter(ClientDetailsEntity client) { 119 120 try { 121 if (client.getJwks() != null) { 122 return jwksEncrypters.get(client.getJwks()); 123 } else if (!Strings.isNullOrEmpty(client.getJwksUri())) { 124 return jwksUriCache.getEncrypter(client.getJwksUri()); 125 } else { 126 return null; 127 } 128 } catch (UncheckedExecutionException | ExecutionException e) { 129 logger.error("Problem loading client encrypter", e); 130 return null; 131 } 132 133 } 134 135 136 private class JWKSetEncryptorBuilder extends CacheLoader<JWKSet, JWTEncryptionAndDecryptionService> { 137 138 @Override 139 public JWTEncryptionAndDecryptionService load(JWKSet key) throws Exception { 140 return new DefaultJWTEncryptionAndDecryptionService(new JWKSetKeyStore(key)); 141 } 142 143 } 144 145 private class JWKSetVerifierBuilder extends CacheLoader<JWKSet, JWTSigningAndValidationService> { 146 147 @Override 148 public JWTSigningAndValidationService load(JWKSet key) throws Exception { 149 return new DefaultJWTSigningAndValidationService(new JWKSetKeyStore(key)); 150 } 151 152 } 153 154 155}