SecurityManager.java revision 36cec39e526f0e72723c7645181bac09e726b94f
5693N/A/*
5693N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5693N/A *
5693N/A * Copyright 2013-2014 ForgeRock AS.
5693N/A *
5693N/A * The contents of this file are subject to the terms
5693N/A * of the Common Development and Distribution License
5693N/A * (the License). You may not use this file except in
5693N/A * compliance with the License.
5693N/A *
5693N/A * You can obtain a copy of the License at
5693N/A * http://forgerock.org/license/CDDLv1.0.html
5693N/A * See the License for the specific language governing
5693N/A * permission and limitations under the License.
5693N/A *
5693N/A * When distributing Covered Code, include this CDDL
5693N/A * Header Notice in each file and include the License file
5693N/A * at http://forgerock.org/license/CDDLv1.0.html
5693N/A * If applicable, add the following below the CDDL Header,
5693N/A * with the fields enclosed by brackets [] replaced by
5693N/A * your own identifying information:
5693N/A * "Portions Copyrighted [year] [name of copyright owner]"
5693N/A */
5693N/A
5693N/Apackage org.forgerock.openidm.security;
5693N/A
5693N/Aimport java.security.Security;
5693N/A
5693N/Aimport javax.net.ssl.KeyManager;
5693N/Aimport javax.net.ssl.KeyManagerFactory;
5693N/Aimport javax.net.ssl.SSLContext;
5693N/Aimport javax.net.ssl.TrustManager;
5693N/Aimport javax.net.ssl.TrustManagerFactory;
5693N/A
5693N/Aimport org.apache.felix.scr.annotations.Activate;
5693N/Aimport org.apache.felix.scr.annotations.Component;
5693N/Aimport org.apache.felix.scr.annotations.ConfigurationPolicy;
5693N/Aimport org.apache.felix.scr.annotations.Deactivate;
5693N/Aimport org.apache.felix.scr.annotations.Properties;
5693N/Aimport org.apache.felix.scr.annotations.Property;
5693N/Aimport org.apache.felix.scr.annotations.Reference;
5693N/Aimport org.apache.felix.scr.annotations.Service;
5693N/Aimport org.bouncycastle.jce.provider.BouncyCastleProvider;
5693N/Aimport org.forgerock.json.fluent.JsonValue;
5693N/Aimport org.forgerock.json.resource.ActionRequest;
5693N/Aimport org.forgerock.json.resource.CreateRequest;
5693N/Aimport org.forgerock.json.resource.DeleteRequest;
5693N/Aimport org.forgerock.json.resource.InternalServerErrorException;
5693N/Aimport org.forgerock.json.resource.PatchRequest;
5693N/Aimport org.forgerock.json.resource.QueryRequest;
5693N/Aimport org.forgerock.json.resource.QueryResultHandler;
5693N/Aimport org.forgerock.json.resource.ReadRequest;
5693N/Aimport org.forgerock.json.resource.RequestHandler;
5693N/Aimport org.forgerock.json.resource.Requests;
5693N/Aimport org.forgerock.json.resource.Resource;
5693N/Aimport org.forgerock.json.resource.ResourceException;
5693N/Aimport org.forgerock.json.resource.ResultHandler;
5693N/Aimport org.forgerock.json.resource.RootContext;
5693N/Aimport org.forgerock.json.resource.Router;
5693N/Aimport org.forgerock.json.resource.ServerContext;
5693N/Aimport org.forgerock.json.resource.UpdateRequest;
5693N/Aimport org.forgerock.openidm.cluster.ClusterUtils;
5693N/Aimport org.forgerock.openidm.core.IdentityServer;
5693N/Aimport org.forgerock.openidm.core.ServerConstants;
5693N/Aimport org.forgerock.openidm.crypto.factory.CryptoUpdateService;
5693N/Aimport org.forgerock.openidm.jetty.Config;
5693N/Aimport org.forgerock.openidm.jetty.Param;
5693N/Aimport org.forgerock.openidm.repo.RepositoryService;
5693N/Aimport org.forgerock.openidm.security.impl.CertificateResourceProvider;
5693N/Aimport org.forgerock.openidm.security.impl.EntryResourceProvider;
5693N/Aimport org.forgerock.openidm.security.impl.JcaKeyStoreHandler;
5693N/Aimport org.forgerock.openidm.security.impl.KeystoreResourceProvider;
5693N/Aimport org.forgerock.openidm.security.impl.PrivateKeyResourceProvider;
5693N/Aimport org.osgi.framework.Constants;
5693N/Aimport org.osgi.service.component.ComponentContext;
5693N/Aimport org.slf4j.Logger;
5693N/Aimport org.slf4j.LoggerFactory;
5693N/A
5693N/A/**
5693N/A * A Security Manager Service which handles operations on the java security
5693N/A * keystore and truststore files.
5693N/A */
5693N/A@Component(name = SecurityManager.PID, policy = ConfigurationPolicy.IGNORE, metatype = true,
5693N/A description = "OpenIDM Security Management Service", immediate = true)
5693N/A@Service
5693N/A@Properties({
5693N/A @Property(name = Constants.SERVICE_VENDOR, value = ServerConstants.SERVER_VENDOR_NAME),
5693N/A @Property(name = Constants.SERVICE_DESCRIPTION, value = "Security Management Service"),
5693N/A @Property(name = ServerConstants.ROUTER_PREFIX, value = "/security/*") })
5693N/Apublic class SecurityManager implements RequestHandler, KeyStoreManager {
5693N/A
5693N/A public static final String PID = "org.forgerock.openidm.security";
5693N/A
5693N/A /**
5693N/A * Setup logging for the {@link SecurityManager}.
5693N/A */
5693N/A private final static Logger logger = LoggerFactory.getLogger(SecurityManager.class);
5693N/A
5693N/A @Reference
protected RepositoryService repoService;
@Reference
private CryptoUpdateService cryptoUpdateService;
private final Router router = new Router();
private KeyStoreHandler trustStoreHandler = null;
private KeyStoreHandler keyStoreHandler = null;
@Activate
void activate(ComponentContext compContext) throws Exception {
logger.debug("Activating Security Management Service {}", compContext);
// Add the Bouncy Castle provider
Security.addProvider(new BouncyCastleProvider());
String keyStoreType = Param.getKeystoreType();
String keyStoreLocation = Param.getKeystoreLocation();
String keyStorePassword = Param.getKeystorePassword(false);
String trustStoreType = Param.getTruststoreType();
String trustStoreLocation = Param.getTruststoreLocation();
String trustStorePassword = Param.getTruststorePassword(false);
// Set System properties
if (System.getProperty("javax.net.ssl.keyStore") == null) {
System.setProperty("javax.net.ssl.keyStore", keyStoreLocation);
System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
System.setProperty("javax.net.ssl.keyStoreType", keyStoreType);
}
if (System.getProperty("javax.net.ssl.trustStore") == null) {
System.setProperty("javax.net.ssl.trustStore", trustStoreLocation);
System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
System.setProperty("javax.net.ssl.trustStoreType", trustStoreType);
}
keyStoreHandler = new JcaKeyStoreHandler(keyStoreType, keyStoreLocation, keyStorePassword);
KeystoreResourceProvider keystoreProvider = new KeystoreResourceProvider("keystore", keyStoreHandler, this, repoService);
EntryResourceProvider keystoreCertProvider = new CertificateResourceProvider("keystore", keyStoreHandler, this, repoService);
EntryResourceProvider privateKeyProvider = new PrivateKeyResourceProvider("keystore", keyStoreHandler, this, repoService);
router.addRoute("/keystore", keystoreProvider);
router.addRoute("/keystore/cert", keystoreCertProvider);
router.addRoute("/keystore/privatekey", privateKeyProvider);
trustStoreHandler = new JcaKeyStoreHandler(trustStoreType, trustStoreLocation, trustStorePassword);
KeystoreResourceProvider truststoreProvider = new KeystoreResourceProvider("truststore", trustStoreHandler, this, repoService);
EntryResourceProvider truststoreCertProvider = new CertificateResourceProvider("truststore", trustStoreHandler, this, repoService);
router.addRoute("/truststore", truststoreProvider);
router.addRoute("/truststore/cert", truststoreCertProvider);
String instanceType = IdentityServer.getInstance().getProperty("openidm.instance.type", ClusterUtils.TYPE_STANDALONE);
String propValue = Param.getProperty("openidm.https.keystore.cert.alias");
String privateKeyAlias = (propValue == null) ? "openidm-localhost" : propValue;
try {
if (instanceType.equals(ClusterUtils.TYPE_CLUSTERED_ADDITIONAL)) {
// Load keystore and truststore from the repository
keystoreProvider.loadStoreFromRepo();
truststoreProvider.loadStoreFromRepo();
// Reload the SSL context
reload();
// Update CryptoService
cryptoUpdateService.updateKeySelector(keyStoreHandler.getStore(), keyStorePassword);
} else {
// Check if the default alias exists in keystore and truststore
final boolean defaultPrivateKeyEntryExists = privateKeyProvider.hasEntry(privateKeyAlias);
final boolean defaultTruststoreEntryExists = truststoreCertProvider.hasEntry(privateKeyAlias);
if (!defaultPrivateKeyEntryExists && !defaultTruststoreEntryExists) {
// dafault keystore/truststore entries do not exist
// Create the default private key
createDefaultKeystoreAndTruststoreEntries(privateKeyAlias, privateKeyProvider, keystoreCertProvider,
truststoreCertProvider);
// Reload the SSL context
reload();
Config.updateConfig(null);
} else if (!defaultPrivateKeyEntryExists && defaultTruststoreEntryExists) {
// no default keystore entry, but truststore has default entry
// this should only happen if the enduser is manually editing the keystore/truststore
logger.error("Keystore and truststore out of sync. The keystore doesn't contain the default "
+ "entry, but the truststore does.");
throw new InternalServerErrorException("Keystore and truststore out of sync. The keystore "
+ "doesn't contain the default entry, but the truststore does.");
} else if (defaultPrivateKeyEntryExists && !defaultTruststoreEntryExists) {
// default keystore entry exists, but truststore default entry does not exist
// this should only happen if the enduser is manually editing the keystore/truststore
logger.error("Keystore and truststore out of sync. The keystore contains the default entry, but "
+ "the truststore doesn't");
throw new InternalServerErrorException("Keystore and truststore out of sync. The keystore "
+ "contains the default entry, but the truststore doesn't");
} else {
// the default entry exists in both the truststore and keystore
// do nothing
}
// If this is the first/primary node in a cluster, then save the keystore and truststore to the repository
if (instanceType.equals(ClusterUtils.TYPE_CLUSTERED_FIRST)) {
keystoreProvider.saveStoreToRepo();
truststoreProvider.saveStoreToRepo();
}
}
} catch (Exception e) {
logger.warn("Error initializing keys", e);
}
}
@Deactivate
void deactivate(ComponentContext compContext) {
logger.debug("Deactivating Security Management Service {}", compContext);
router.removeAllRoutes();
}
// ----- Implementation of KeyStoreManager interface
public void reload() throws Exception {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStoreHandler.getStore());
TrustManager [] trustManagers = tmf.getTrustManagers();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStoreHandler.getStore(), keyStoreHandler.getPassword().toCharArray());
KeyManager [] keyManagers = kmf.getKeyManagers();
SSLContext context = SSLContext.getInstance("SSL");
context.init(keyManagers, trustManagers, null);
SSLContext.setDefault(context);
}
// ----- Implementation of RequestHandler interface
@Override
public void handleAction(final ServerContext context, final ActionRequest request,
final ResultHandler<JsonValue> handler) {
router.handleAction(context, request, handler);
}
@Override
public void handleCreate(final ServerContext context, final CreateRequest request,
final ResultHandler<Resource> handler) {
router.handleCreate(context, request, handler);
}
@Override
public void handleDelete(final ServerContext context, final DeleteRequest request,
final ResultHandler<Resource> handler) {
router.handleDelete(context, request, handler);
}
@Override
public void handlePatch(final ServerContext context, final PatchRequest request,
final ResultHandler<Resource> handler) {
router.handlePatch(context, request, handler);
}
@Override
public void handleQuery(final ServerContext context, final QueryRequest request,
final QueryResultHandler handler) {
router.handleQuery(context, request, handler);
}
@Override
public void handleRead(final ServerContext context, final ReadRequest request,
final ResultHandler<Resource> handler) {
router.handleRead(context, request, handler);
}
@Override
public void handleUpdate(final ServerContext context, final UpdateRequest request,
final ResultHandler<Resource> handler) {
router.handleUpdate(context, request, handler);
}
private void createDefaultKeystoreAndTruststoreEntries(final String alias,
final EntryResourceProvider privateKeyProvider,
final EntryResourceProvider keystoreCertProvider,
final EntryResourceProvider truststoreCertProvider)
throws Exception {
//create the keystore default entry
privateKeyProvider.createDefaultEntry(alias);
final DefaultEntriesResultHandler defaultEntriesResultHandler = new DefaultEntriesResultHandler();
//get the keystore default entry cert
final ReadRequest readRequest = Requests.newReadRequest("/keystore/cert");
keystoreCertProvider.readInstance(
new ServerContext(new RootContext()), alias, readRequest, defaultEntriesResultHandler);
//add the keystore default entry cert to the truststore
final CreateRequest createRequest = Requests.newCreateRequest("/truststore/cert", alias,
defaultEntriesResultHandler.result.getContent());
truststoreCertProvider.createInstance(
new ServerContext(new RootContext()), createRequest, defaultEntriesResultHandler);
}
private final static class DefaultEntriesResultHandler implements ResultHandler<Resource> {
private Resource result;
@Override
public void handleError(ResourceException e) {
logger.error("Unable to create default keystore/truststore entries", e);
throw new RuntimeException("Unable to create default keystore/truststore entries", e);
}
@Override
public void handleResult(Resource resource) {
result = resource;
}
}
}