/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.security.authentication;
import java.util.concurrent.atomic.AtomicLong;
import javax.crypto.SecretKey;
import com.emc.storageos.model.property.PropertyInfo;
import com.emc.storageos.security.SignatureHelper;
import com.emc.storageos.security.SignatureKeyGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* key generator for apisvc internal api HMAC key --> this class will also be used for inter vdc
* HMAC key handling. So we should probably rename it to something more generic or maybe simply combine
* this class and its base.
*/
public class InternalApiSignatureKeyGenerator extends SignatureKeyGenerator {
public enum SignatureKeyType {
INTERNAL_API, INTERVDC_API
};
private static final String SYSTEM_PROPERTY_INTERNAL_API_ALGO = "internal_api_key_algo";
private static final String SYSTEM_PROPERTY_INTERNAL_API_ALGO_VALUE_NEW = "sha-256";
private static final String DISTRIBUTED_SIGNATURE_KEY_LOCK = "InternalApiKeyLock";
private static final String SIGNATURE_KEY_CONFIG = "InternalApiKeyConfig";
private static final String SIGNATURE_KEY_ID = "global";
private static final String CURRENT_SIGNATURE_INTERNALAPI_KEY = "InternalApiKey"; // current
private static final String NEW_SIGNATURE_INTERNALAPI_KEY = "InternalApiKeyNew"; // new overriding key
private static final String SIGNATURE_INTERVDC_KEY = "InterVDCApiKey";
private static final Logger _log = LoggerFactory.getLogger(InternalApiSignatureKeyGenerator.class);
// keeping internal api algo private and vdc algo public as a hint
// note: internal api keys and algorithm should not be dealt with directly.
// inter vdc keys do get exposed.
public static final String CURRENT_INTERNAL_API_SIGN_ALGO = "HmacSHA1"; // currently used in zk
public static final String NEW_INTERNAL_API_SIGN_ALGO = "HmacSHA256"; // new key. If available, takes precedence over current. (current
// becomes obsolete)
public static final String CURRENT_INTERVDC_API_SIGN_ALGO = "HmacSHA256";
private SecretKey _internalApiCurrentKey = null;
private SecretKey _interVDCCurrentKey = null;
boolean _initialized = false;
private static AtomicLong _lastUpdated = new AtomicLong();
private static int DEFAULT_MAX_KEY_STALE_TIME_IN_HRS = 6;
private static int _maxKeyStaleTimeInHrs = DEFAULT_MAX_KEY_STALE_TIME_IN_HRS;
public void setMaxKeyStaleTimeInHrs(int hrs) {
_maxKeyStaleTimeInHrs = hrs;
}
@Override
public String getDistributedSignatureKeyLock() {
return DISTRIBUTED_SIGNATURE_KEY_LOCK;
}
@Override
public String getSignatureKeyId() {
return SIGNATURE_KEY_ID;
}
@Deprecated
@Override
public String getSignatureKey() {
return CURRENT_SIGNATURE_INTERNALAPI_KEY;
}
@Override
public String getSignatureKeyConfig() {
return SIGNATURE_KEY_CONFIG;
}
@Override
public Logger getLogger() {
return _log;
}
/**
* Reads keys from coordinator.
*
* @throws Exception if current key can't be found
* @return the previous key if available or current key
*/
public synchronized void loadKeys() {
try {
PropertyInfo props = _coordinator.getPropertyInfo();
String internalApiAlgoOverride = null;
if (props != null) {
internalApiAlgoOverride = props.getProperty(SYSTEM_PROPERTY_INTERNAL_API_ALGO);
}
_log.debug("Internal Api algo override property: " + internalApiAlgoOverride);
if (internalApiAlgoOverride != null && internalApiAlgoOverride.equals(SYSTEM_PROPERTY_INTERNAL_API_ALGO_VALUE_NEW)) {
_internalApiCurrentKey = getSignatureKey2(NEW_SIGNATURE_INTERNALAPI_KEY, NEW_INTERNAL_API_SIGN_ALGO);
deleteSignatureKey(CURRENT_SIGNATURE_INTERNALAPI_KEY);
} else {
_internalApiCurrentKey = getSignatureKey2(CURRENT_SIGNATURE_INTERNALAPI_KEY,
CURRENT_INTERNAL_API_SIGN_ALGO);
}
// get inter vdc key
_interVDCCurrentKey = getSignatureKey2(SIGNATURE_INTERVDC_KEY,
CURRENT_INTERVDC_API_SIGN_ALGO);
} catch (Exception e) {
throw new IllegalStateException("Exception while retrieving key", e);
}
if (_internalApiCurrentKey == null) {
throw new IllegalStateException("Key was null / Unable to get current internal api key.");
}
if (_interVDCCurrentKey == null) {
throw new IllegalStateException("Key was null / Unable to get current inter vdc api key.");
}
_initialized = true;
_lastUpdated.set(System.currentTimeMillis());
return;
}
public SecretKey getSignatureKey(SignatureKeyType type) {
reloadKeysIfNeeded();
switch (type) {
case INTERNAL_API:
_log.debug("Api Key uses {}", _internalApiCurrentKey.getAlgorithm().toString());
return _internalApiCurrentKey;
case INTERVDC_API:
return _interVDCCurrentKey;
}
return null;
}
/**
* Indicates if the bean was initialized at least once. For optimization where one does not
* want to call loadKeys() if a key is already loaded.
*
* @return
*/
public boolean isInitialized() {
return _initialized;
}
/**
* Reloads keys from coordinator if it's been maxKeyStaleTimeInHrs
*/
private void reloadKeysIfNeeded() {
long now = System.currentTimeMillis();
if (!_initialized || ((now - _lastUpdated.get()) / (1000 * 60 * 60) > _maxKeyStaleTimeInHrs)) {
try {
_log.info("Stale time reached, reloading keys");
loadKeys();
_log.info("Reloaded keys successfully");
} catch (Exception e) {
_log.error("Could not reload the keys", e);
}
} else {
_log.debug("Reload not needed");
}
return;
}
/**
* signs the contents of String. Signs with the internal api key
*
* @param buf content to sign
* @return signature empty string if signature could not be computed
*/
public String sign(String buf) {
return sign(buf, SignatureKeyType.INTERNAL_API);
}
public String sign(String buf, SignatureKeyType type) {
reloadKeysIfNeeded();
SecretKey key = getSignatureKey(type);
if (key == null) {
_log.error("Null key while signing");
return "";
}
return SignatureHelper.sign2(buf.getBytes(), key, key.getAlgorithm());
}
}