dk.grinn.keycloak.migration.boundary.RealmKeyController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of keycloak-migrate-spi Show documentation
Show all versions of keycloak-migrate-spi Show documentation
Keycloak migrate extending the keycloak jpa model to include migrate
versioning. And addtional REST end-points for migration purposes.
package dk.grinn.keycloak.migration.boundary;
/*-
* #%L
* Keycloak : Migrate : Spi
* %%
* Copyright (C) 2019 Jonas Grann & Kim Jersin
* %%
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* #L%
*/
import dk.grinn.keycloak.migration.entities.CreateRealmKey;
import org.jboss.logging.Logger;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.PemUtils;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.RealmModel;
import javax.persistence.EntityManager;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.util.Base64;
/**
*
* @author @author Nicolai Gjøderum <[email protected]>
*/
public class RealmKeyController {
private static final Logger LOG = Logger.getLogger(RealmKeyController.class);
protected static final String PRIVATE_KEY_CONFIG_KEY = "privateKey";
protected static final String CERTIFICATE_CONFIG_KEY = "certificate";
private EntityManager em;
private GkcadmRealmAttributeController controller;
public RealmKeyController(EntityManager em) {
this.em = em;
this.controller = new GkcadmRealmAttributeController(em);
}
public static void disableRsaGenerated(RealmModel realmModel) {
realmModel.getComponents().stream().filter(componentModel -> componentModel.getName().equals("rsa-generated")).forEach(componentModel -> {
componentModel.getConfig().putSingle("active", "false");
componentModel.getConfig().putSingle("enabled", "false");
realmModel.updateComponent(componentModel);
});
}
public void setRealmKey(RealmModel realmModel, CreateRealmKey createRealmKey) {
String realmName = realmModel.getName();
CertAndKeyPair pair = getCertAndKeyPairFromEnvironment(createRealmKey);
if(pair.isEmpty()){
pair = getCertAndKeyPairFromPath(createRealmKey);
}
if(createRealmKey.isReuse() && pair.isEmpty()) {
pair = getCertAndKeyPairFromRealm(realmName);
}
if(pair.isEmpty()) {
pair = generateNewCertAndKeyPair(createRealmKey);
}
createReusableRSACertificate(realmModel, createRealmKey, realmName, pair);
}
private CertAndKeyPair generateNewCertAndKeyPair(CreateRealmKey createRealmKey) {
LOG.info("Generating new realm key");
KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048);
Certificate genCert = CertificateUtils.generateV1SelfSignedCertificate(keyPair, createRealmKey.getSubject());
return new CertAndKeyPair(PemUtils.encodeCertificate(genCert), PemUtils.encodeKey(keyPair.getPrivate()));
}
private CertAndKeyPair getCertAndKeyPairFromRealm(String realmName) {
LOG.info("Signaled re-using realm key");
return new CertAndKeyPair(controller.getAttribute(realmName, CERTIFICATE_CONFIG_KEY), controller.getAttribute(realmName, PRIVATE_KEY_CONFIG_KEY));
}
private CertAndKeyPair getCertAndKeyPairFromPath(CreateRealmKey createRealmKey) {
char[] password = createRealmKey.getPassword();
String alias = createRealmKey.getAlias();
String keyPath = createRealmKey.getPath();
if( isNullOrEmpty(keyPath) || password == null || isNullOrEmpty(alias)){
return CertAndKeyPair.EMPTY;
} else {
Path path = Paths.get(createRealmKey.getPath());
LOG.info("Using realm key from path = " + path.toString());
KeyStore ks = getKeyStore(path, password);
return getCertAndKeyPairFromKeystore(ks, password, alias);
}
}
private CertAndKeyPair getCertAndKeyPairFromKeystore(KeyStore ks, char[] password, String alias) {
try {
Certificate cert = ks.getCertificate(alias);
PrivateKey pk = (PrivateKey) ks.getKey(alias, password);
String certificate = Base64.getEncoder().encodeToString(cert.getEncoded());
String privateKey = Base64.getEncoder().encodeToString(pk.getEncoded());
return new CertAndKeyPair(certificate, privateKey);
} catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateEncodingException e) {
throw new RuntimeException("Could not load certificate.");
}
}
private CertAndKeyPair getCertAndKeyPairFromEnvironment(CreateRealmKey createRealmKey) {
CertAndKeyPair pair = new CertAndKeyPair(createRealmKey.getCertificate(), createRealmKey.getPrivateKey());
if(!pair.isEmpty()){
LOG.info("Using realm key from ENV");
}
return pair;
}
private void createReusableRSACertificate(RealmModel realmModel, CreateRealmKey createRealmKey, String realmName, CertAndKeyPair pair) {
controller.putAttribute(realmName, PRIVATE_KEY_CONFIG_KEY, pair.getPrivateKey());
controller.putAttribute(realmName, CERTIFICATE_CONFIG_KEY, pair.getCertificate());
ComponentModel rsaComponent = createRsaComponent(createRealmKey.getName(), realmModel.getId(), pair.getPrivateKey(), pair.getCertificate(), createRealmKey.getPriority());
realmModel.addComponentModel(rsaComponent);
}
private ComponentModel createRsaComponent(String name, String realmId, String privateKey, String certificate, long priority) {
ComponentModel comp = new ComponentModel();
comp.setName(name);
comp.setParentId(realmId);
comp.setProviderId("rsa");
comp.setProviderType("org.keycloak.keys.KeyProvider");
comp.setConfig(new MultivaluedHashMap<>());
comp.getConfig().putSingle("active", "true");
comp.getConfig().putSingle("enabled", "true");
comp.getConfig().putSingle("priority", String.valueOf(priority));
comp.getConfig().putSingle(PRIVATE_KEY_CONFIG_KEY, privateKey);
comp.getConfig().putSingle(CERTIFICATE_CONFIG_KEY, certificate);
comp.getConfig().putSingle("algorithm", "RS256");
return comp;
}
private KeyStore getKeyStore(Path path, char[] password) {
if(Files.exists(path)){
String type = getType(path);
return loadKeyStore(path, type, password);
} else {
throw new IllegalArgumentException("The specified certificate file does not exist.");
}
}
private String getType(Path path) {
if(path.getFileName().toString().toLowerCase().endsWith(".p12")){
return "PKCS12";
} else if(path.getFileName().toString().toLowerCase().endsWith(".jks")){
return KeyStore.getDefaultType();
} else {
throw new IllegalArgumentException("Invalid certificate type. Allowed types: [pkcs12, jks]");
}
}
private KeyStore loadKeyStore(Path path, String type, char[] password) {
try(InputStream is = Files.newInputStream(path)) {
KeyStore ks = KeyStore.getInstance(type);
ks.load(is, password);
return ks;
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
throw new RuntimeException("Could not load certificate " + path, e);
}
}
private static boolean isNullOrEmpty(String string){
return string == null || string.trim().isEmpty();
}
private static class CertAndKeyPair{
static final CertAndKeyPair EMPTY = new CertAndKeyPair(null, null);
private String certificate;
private String privateKey;
CertAndKeyPair(String certificate, String privateKey) {
this.certificate = certificate;
this.privateKey = privateKey;
}
String getCertificate() {
return certificate;
}
String getPrivateKey() {
return privateKey;
}
boolean isEmpty(){
return isNullOrEmpty(certificate) || isNullOrEmpty(privateKey);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy