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}