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 *******************************************************************************/
016package org.mitre.jwt.signer.service.impl;
017
018import java.security.NoSuchAlgorithmException;
019import java.security.spec.InvalidKeySpecException;
020import java.util.Map;
021import java.util.concurrent.ExecutionException;
022import java.util.concurrent.TimeUnit;
023
024import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
025import org.mitre.oauth2.model.ClientDetailsEntity;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028import org.springframework.stereotype.Service;
029
030import com.google.common.base.Strings;
031import com.google.common.cache.CacheBuilder;
032import com.google.common.cache.CacheLoader;
033import com.google.common.cache.LoadingCache;
034import com.google.common.collect.ImmutableMap;
035import com.google.common.util.concurrent.UncheckedExecutionException;
036import com.nimbusds.jose.jwk.JWK;
037import com.nimbusds.jose.jwk.KeyUse;
038import com.nimbusds.jose.jwk.OctetSequenceKey;
039import com.nimbusds.jose.util.Base64URL;
040
041/**
042 * Creates and caches symmetrical validators for clients based on client secrets.
043 *
044 * @author jricher
045 *
046 */
047@Service
048public class SymmetricKeyJWTValidatorCacheService {
049
050        /**
051         * Logger for this class
052         */
053        private static final Logger logger = LoggerFactory.getLogger(SymmetricKeyJWTValidatorCacheService.class);
054
055        private LoadingCache<String, JWTSigningAndValidationService> validators;
056
057
058        public SymmetricKeyJWTValidatorCacheService() {
059                validators = CacheBuilder.newBuilder()
060                                .expireAfterAccess(24, TimeUnit.HOURS)
061                                .maximumSize(100)
062                                .build(new SymmetricValidatorBuilder());
063        }
064
065
066        /**
067         * Create a symmetric signing and validation service for the given client
068         *
069         * @param client
070         * @return
071         */
072        public JWTSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity client) {
073
074                if (client == null) {
075                        logger.error("Couldn't create symmetric validator for null client");
076                        return null;
077                }
078
079                if (Strings.isNullOrEmpty(client.getClientSecret())) {
080                        logger.error("Couldn't create symmetric validator for client " + client.getClientId() + " without a client secret");
081                        return null;
082                }
083
084                try {
085                        return validators.get(client.getClientSecret());
086                } catch (UncheckedExecutionException ue) {
087                        logger.error("Problem loading client validator", ue);
088                        return null;
089                } catch (ExecutionException e) {
090                        logger.error("Problem loading client validator", e);
091                        return null;
092                }
093
094        }
095
096        public class SymmetricValidatorBuilder extends CacheLoader<String, JWTSigningAndValidationService> {
097                @Override
098                public JWTSigningAndValidationService load(String key) throws Exception {
099                        try {
100
101                                String id = "SYMMETRIC-KEY";
102                                JWK jwk = new OctetSequenceKey.Builder(Base64URL.encode(key))
103                                        .keyUse(KeyUse.SIGNATURE)
104                                        .keyID(id)
105                                        .build();
106                                Map<String, JWK> keys = ImmutableMap.of(id, jwk);
107                                JWTSigningAndValidationService service = new DefaultJWTSigningAndValidationService(keys);
108
109                                return service;
110
111                        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
112                                logger.error("Couldn't create symmetric validator for client", e);
113                        }
114
115                        throw new IllegalArgumentException("Couldn't create symmetric validator for client");
116                }
117
118        }
119
120}