/* dCache - http://www.dcache.org/
*
* Copyright (C) 2014 Deutsches Elektronen-Synchrotron
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.gsi;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* A source of KeyPairs, where successive requests for the same length will
* return the same KeyPair. This class takes inspiration from
* org.globus.gsi.gssapi.KeyPairCache from JGlobus but uses Guava's Cache
* support rather than implementing a custom cache.
*/
public class KeyPairCache
{
private static final Logger LOG = LoggerFactory.getLogger(KeyPairCache.class);
private static final String DEFAULT_ALGORITHM = "RSA";
private static final String DEFAULT_PROVIDER = "BC";
// Number of days of inactivity after which a cached entry is removed
private static final int EXPIRE_AFTER = 1;
private static final Executor _executor = Executors.newCachedThreadPool(
new ThreadFactoryBuilder().setNameFormat("KeyPair-generator-%d").setDaemon(true).build());
private final LoadingCache<Integer,KeyPair> _cache;
private String algorithm = DEFAULT_ALGORITHM;
private String provider = DEFAULT_PROVIDER;
public KeyPairCache(long lifetime, TimeUnit unit)
{
if(lifetime > 0) {
_cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(EXPIRE_AFTER, TimeUnit.DAYS)
.refreshAfterWrite(lifetime, unit)
.build(
new CacheLoader<Integer,KeyPair>() {
@Override
public KeyPair load(Integer keySize) throws
NoSuchAlgorithmException,
NoSuchProviderException
{
return generate(keySize);
}
@Override
public ListenableFuture<KeyPair> reload(final
Integer keySize, KeyPair previous)
{
ListenableFutureTask<KeyPair> task =
ListenableFutureTask.create(new Callable<KeyPair>()
{
@Override
public KeyPair call() throws Exception
{
return generate(keySize);
}
});
_executor.execute(task);
return task;
}
}
);
} else {
_cache = null;
}
}
public String getAlgorithm()
{
return algorithm;
}
public void setAlgorithm(String value)
{
algorithm = value;
}
public String getProvider()
{
return provider;
}
public void setProvider(String value)
{
provider = value;
}
public KeyPair getKeyPair(int bits)
throws NoSuchAlgorithmException, NoSuchProviderException {
if (_cache == null) {
return generate(bits);
} else {
try {
return _cache.get(bits);
} catch (ExecutionException e) {
// propagate
throw new RuntimeException();
}
}
}
private KeyPair generate(int bits) throws NoSuchAlgorithmException,
NoSuchProviderException
{
LOG.debug("Generating KeyPair for {} bits", bits);
KeyPairGenerator generator =
KeyPairGenerator.getInstance(this.algorithm, this.provider);
generator.initialize(bits);
return generator.generateKeyPair();
}
}