![JAR search and dependency download from the Maven repository](/logo.png)
co.cask.common.security.authentication.AbstractKeyManager Maven / Gradle / Ivy
/*
* Copyright © 2014 Cask Data, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package co.cask.common.security.authentication;
import co.cask.common.Bytes;
import co.cask.common.io.Codec;
import co.cask.common.security.Constants;
import co.cask.common.security.config.SecurityConfiguration;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.AbstractIdleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
/**
* AbstractKeyManager that provides the basic functionality that all key managers share. This includes
* generation of keys and MACs, and validation of MACs. Subclasses are expected to override the init method.
*/
public abstract class AbstractKeyManager extends AbstractIdleService implements KeyManager {
private static final Logger LOG = LoggerFactory.getLogger(AbstractKeyManager.class);
protected ThreadLocal threadLocalMac;
protected KeyGenerator keyGenerator;
protected volatile KeyIdentifier currentKey;
protected final String keyAlgo;
protected final int keyLength;
/**
* Time duration (in milliseconds) after which an active secret key should be retired. A value or zero or less
* means no expiration.
*/
protected long keyExpirationPeriod = 0;
/**
* An AbstractKeyManager that has common functionality of all keymanagers.
* @param conf
*/
public AbstractKeyManager(SecurityConfiguration conf) {
this(conf.get(Constants.TOKEN_DIGEST_ALGO),
conf.getInt(Constants.TOKEN_DIGEST_KEY_LENGTH));
}
public AbstractKeyManager(String keyAlgo, int keyLength) {
this.keyAlgo = keyAlgo;
this.keyLength = keyLength;
}
@Override
public final void startUp() throws NoSuchAlgorithmException, IOException {
keyGenerator = KeyGenerator.getInstance(keyAlgo);
keyGenerator.init(keyLength);
threadLocalMac = new ThreadLocal() {
@Override
public Mac initialValue() {
try {
return Mac.getInstance(keyAlgo);
} catch (NoSuchAlgorithmException nsae) {
throw new IllegalArgumentException("Unknown algorithm for secret keys: " + keyAlgo);
}
}
};
doInit();
}
/**
* Extended classes must override this method to initialize/read the key(s) used for signing tokens.
* @throws java.io.IOException
*/
protected abstract void doInit() throws IOException;
/**
* Returns whether or not a key exists for the given unique ID.
*/
protected abstract boolean hasKey(int id);
/**
* Returns the key instance matching a given unique ID.
*/
protected abstract KeyIdentifier getKey(int id);
/**
* Adds a given key instance.
*/
protected abstract void addKey(KeyIdentifier key);
/**
* Generates a new KeyIdentifier and sets that to be the current key being used.
* @return A new KeyIdentifier.
*/
protected final KeyIdentifier generateKey() {
Random rand = new Random();
int nextId;
do {
nextId = rand.nextInt(Integer.MAX_VALUE);
} while (hasKey(nextId));
long now = System.currentTimeMillis();
SecretKey nextKey = keyGenerator.generateKey();
KeyIdentifier keyIdentifier =
new KeyIdentifier(nextKey, nextId, keyExpirationPeriod > 0 ? (now + keyExpirationPeriod) : Long.MAX_VALUE);
addKey(keyIdentifier);
this.currentKey = keyIdentifier;
LOG.info("Changed current key to {}", currentKey);
return keyIdentifier;
}
@Override
public final void validateMAC(Codec codec, Signed signedMessage)
throws InvalidDigestException, InvalidKeyException {
try {
byte[] newDigest = generateMAC(signedMessage.getKeyId(), codec.encode(signedMessage.getMessage()));
if (!Bytes.equals(signedMessage.getDigestBytes(), newDigest)) {
throw new InvalidDigestException("Token signature is not valid!");
}
} catch (IOException ioe) {
throw Throwables.propagate(ioe);
}
}
@Override
public final DigestId generateMAC(byte[] message) throws InvalidKeyException {
KeyIdentifier signingKey = currentKey;
byte[] digest = generateMAC(signingKey.getKey(), message);
return new DigestId(signingKey.getKeyId(), digest);
}
/**
* Computes a digest for the given input message, using the key identified by the given ID.
* @param keyId Identifier of the secret key to use.
* @param message The data over which we should generate a digest.
* @return The computed digest.
* @throws InvalidKeyException If the input {@code keyId} does not match a known key or the key is not accepted
* by the internal {@code Mac} implementation.
*/
protected final byte[] generateMAC(int keyId, byte[] message) throws InvalidKeyException {
KeyIdentifier key = getKey(keyId);
if (key == null) {
throw new InvalidKeyException("No key found for ID " + keyId);
}
return generateMAC(key.getKey(), message);
}
protected final byte[] generateMAC(SecretKey key, byte[] message) throws InvalidKeyException {
Mac mac = threadLocalMac.get();
// TODO: should we only initialize when the key changes?
mac.init(key);
return mac.doFinal(message);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy