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}