001/*******************************************************************************
002 * Copyright 2014 The MITRE Corporation
003 *   and the MIT Kerberos and Internet Trust Consortium
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *   http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *******************************************************************************/
017package org.mitre.jwt.signer.service.impl;
018
019import java.security.NoSuchAlgorithmException;
020import java.security.spec.InvalidKeySpecException;
021import java.util.Map;
022import java.util.concurrent.ExecutionException;
023import java.util.concurrent.TimeUnit;
024
025import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
026import org.mitre.oauth2.model.ClientDetailsEntity;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029import org.springframework.stereotype.Service;
030
031import com.google.common.base.Strings;
032import com.google.common.cache.CacheBuilder;
033import com.google.common.cache.CacheLoader;
034import com.google.common.cache.LoadingCache;
035import com.google.common.collect.ImmutableMap;
036import com.google.common.util.concurrent.UncheckedExecutionException;
037import com.nimbusds.jose.jwk.JWK;
038import com.nimbusds.jose.jwk.KeyUse;
039import com.nimbusds.jose.jwk.OctetSequenceKey;
040import com.nimbusds.jose.util.Base64URL;
041
042/**
043 * Creates and caches symmetrical validators for clients based on client secrets.
044 * 
045 * @author jricher
046 *
047 */
048@Service
049public class SymmetricCacheService {
050
051        private static Logger logger = LoggerFactory.getLogger(SymmetricCacheService.class);
052
053        private LoadingCache<String, JwtSigningAndValidationService> validators;
054
055
056        public SymmetricCacheService() {
057                validators = CacheBuilder.newBuilder()
058                                .expireAfterAccess(24, TimeUnit.HOURS)
059                                .maximumSize(100)
060                                .build(new SymmetricValidatorBuilder());
061        }
062
063
064        /**
065         * Create a symmetric signing and validation service for the given client
066         * 
067         * @param client
068         * @return
069         */
070        public JwtSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity client) {
071
072                if (client == null) {
073                        logger.error("Couldn't create symmetric validator for null client");
074                        return null;
075                }
076
077                if (Strings.isNullOrEmpty(client.getClientSecret())) {
078                        logger.error("Couldn't create symmetric validator for client " + client.getClientId() + " without a client secret");
079                        return null;
080                }
081
082                try {
083                        return validators.get(client.getClientSecret());
084                } catch (UncheckedExecutionException ue) {
085                        logger.error("Problem loading client validator", ue);
086                        return null;
087                } catch (ExecutionException e) {
088                        logger.error("Problem loading client validator", e);
089                        return null;
090                }
091
092        }
093
094        public class SymmetricValidatorBuilder extends CacheLoader<String, JwtSigningAndValidationService> {
095                @Override
096                public JwtSigningAndValidationService load(String key) throws Exception {
097                        try {
098
099                                String id = "SYMMETRIC-KEY";
100
101                                JWK jwk = new OctetSequenceKey(Base64URL.encode(key), KeyUse.SIGNATURE, null, null, id, null, null, null);
102                                Map<String, JWK> keys = ImmutableMap.of(id, jwk);
103                                JwtSigningAndValidationService service = new DefaultJwtSigningAndValidationService(keys);
104
105                                return service;
106
107                        } catch (NoSuchAlgorithmException e) {
108                                logger.error("Couldn't create symmetric validator for client", e);
109                        } catch (InvalidKeySpecException e) {
110                                logger.error("Couldn't create symmetric validator for client", e);
111                        }
112
113                        throw new IllegalArgumentException("Couldn't create symmetric validator for client");
114                }
115
116        }
117
118}