keycloak-aplcache
Changes
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java 6(+6 -0)
Details
diff --git a/core/src/main/java/org/keycloak/util/CertificateUtils.java b/core/src/main/java/org/keycloak/util/CertificateUtils.java
new file mode 100755
index 0000000..ae18376
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/CertificateUtils.java
@@ -0,0 +1,63 @@
+package org.keycloak.util;
+
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.x509.X509V1CertificateGenerator;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
+import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
+
+import javax.security.auth.x500.X500Principal;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CertificateUtils {
+ public static X509Certificate generateV3Certificate(KeyPair keyPair, PrivateKey caPrivateKey, X509Certificate caCert, String subject) throws Exception {
+
+ X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+ X500Principal subjectName = new X500Principal("CN=" + subject);
+
+ BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
+ certGen.setSerialNumber(serialNumber);
+ certGen.setIssuerDN(caCert.getSubjectX500Principal());
+ certGen.setNotBefore(new Date(System.currentTimeMillis() - 100000));
+ Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.YEAR, 10);
+ certGen.setNotAfter(calendar.getTime());
+ certGen.setSubjectDN(subjectName);
+ certGen.setPublicKey(keyPair.getPublic());
+ certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+
+ certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
+ new AuthorityKeyIdentifierStructure(caCert));
+ certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
+ new SubjectKeyIdentifierStructure(keyPair.getPublic()));
+
+ X509Certificate cert = certGen.generate(caPrivateKey, "BC"); // note: private key of CA
+ return cert;
+ }
+
+ public static X509Certificate generateV1SelfSignedCertificate(KeyPair keyPair, String subject) throws Exception {
+ BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
+ X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+ X500Principal subjectPrincipal = new X500Principal("CN=" + subject);
+ certGen.setSerialNumber(serialNumber);
+ certGen.setIssuerDN(subjectPrincipal);
+ certGen.setNotBefore(new Date(System.currentTimeMillis() - 100000));
+ Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.YEAR, 10);
+ certGen.setNotAfter(calendar.getTime());
+ certGen.setSubjectDN(subjectPrincipal);
+ certGen.setPublicKey(keyPair.getPublic());
+ certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+ X509Certificate cert = certGen.generate(keyPair.getPrivate(), "BC");
+ return cert;
+ }
+}
diff --git a/core/src/main/java/org/keycloak/util/KeystoreUtil.java b/core/src/main/java/org/keycloak/util/KeystoreUtil.java
index d318a5f..76e0f1d 100755
--- a/core/src/main/java/org/keycloak/util/KeystoreUtil.java
+++ b/core/src/main/java/org/keycloak/util/KeystoreUtil.java
@@ -18,4 +18,5 @@ public class KeystoreUtil {
trustStream.close();
return trustStore;
}
+
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index aa06025..4371e26 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -40,6 +40,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private String publicKeyPem;
private String privateKeyPem;
+ private String certificatePem;
private String loginTheme;
private String accountTheme;
@@ -381,4 +382,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
public void setUserFederationProviders(List<UserFederationProviderEntity> userFederationProviders) {
this.userFederationProviders = userFederationProviders;
}
+
+ public String getCertificatePem() {
+ return certificatePem;
+ }
+
+ public void setCertificatePem(String certificatePem) {
+ this.certificatePem = certificatePem;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index d9fbb97..d1895ef 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -4,6 +4,7 @@ import org.keycloak.enums.SslRequired;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -96,6 +97,11 @@ public interface RealmModel extends RoleContainerModel {
void setPublicKey(PublicKey publicKey);
+ X509Certificate getCertificate();
+ void setCertificate(X509Certificate certificate);
+ String getCertificatePem();
+ void setCertificatePem(String certificate);
+
PrivateKey getPrivateKey();
void setPrivateKey(PrivateKey privateKey);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 3e8695c..f0b677e 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -12,6 +12,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
+import org.keycloak.util.CertificateUtils;
import org.keycloak.util.PemUtils;
import java.io.IOException;
@@ -22,6 +23,7 @@ import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.cert.X509Certificate;
import java.util.Set;
import java.util.UUID;
@@ -51,6 +53,19 @@ public final class KeycloakModelUtils {
}
}
+ public static X509Certificate getCertificate(String cert) {
+ if (cert != null) {
+ try {
+ return PemUtils.decodeCertificate(cert);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ return null;
+ }
+ }
+
+
public static PrivateKey getPrivateKey(String privateKeyPem) {
if (privateKeyPem != null) {
try {
@@ -75,6 +90,19 @@ public final class KeycloakModelUtils {
return PemUtils.removeBeginEnd(s);
}
+ public static String getPemFromCertificate(X509Certificate certificate) {
+ StringWriter writer = new StringWriter();
+ PEMWriter pemWriter = new PEMWriter(writer);
+ try {
+ pemWriter.writeObject(certificate);
+ pemWriter.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ String s = writer.toString();
+ return PemUtils.removeBeginEnd(s);
+ }
+
public static void generateRealmKeys(RealmModel realm) {
KeyPair keyPair = null;
try {
@@ -84,6 +112,23 @@ public final class KeycloakModelUtils {
}
realm.setPrivateKey(keyPair.getPrivate());
realm.setPublicKey(keyPair.getPublic());
+ X509Certificate certificate = null;
+ try {
+ certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, realm.getName());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ realm.setCertificate(certificate);
+ }
+
+ public static void generateRealmCertificate(RealmModel realm) {
+ X509Certificate certificate = null;
+ try {
+ certificate = CertificateUtils.generateV1SelfSignedCertificate(new KeyPair(realm.getPublicKey(), realm.getPrivateKey()), realm.getName());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ realm.setCertificate(certificate);
}
public static UserCredentialModel generateSecret(ClientModel app) {
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
index 04ca843..d96a9ea 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
@@ -56,6 +56,7 @@ public class CachedRealm {
private String publicKeyPem;
private String privateKeyPem;
+ private String certificatePem;
private String loginTheme;
private String accountTheme;
@@ -113,6 +114,7 @@ public class CachedRealm {
publicKeyPem = model.getPublicKeyPem();
privateKeyPem = model.getPrivateKeyPem();
+ certificatePem = model.getCertificatePem();
loginTheme = model.getLoginTheme();
accountTheme = model.getAccountTheme();
@@ -328,4 +330,8 @@ public class CachedRealm {
public List<UserFederationProviderModel> getUserFederationProviders() {
return userFederationProviders;
}
+
+ public String getCertificatePem() {
+ return certificatePem;
+ }
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index eeeda26..d471b4a 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -15,6 +15,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -33,6 +34,7 @@ public class RealmAdapter implements RealmModel {
protected RealmCache cache;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
+ protected volatile transient X509Certificate certificate;
public RealmAdapter(CachedRealm cached, CacheRealmProvider cacheSession) {
this.cached = cached;
@@ -332,6 +334,33 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public X509Certificate getCertificate() {
+ if (certificate != null) return certificate;
+ certificate = KeycloakModelUtils.getCertificate(getCertificatePem());
+ return certificate;
+ }
+
+ @Override
+ public void setCertificate(X509Certificate certificate) {
+ this.certificate = certificate;
+ String certPem = KeycloakModelUtils.getPemFromCertificate(certificate);
+ setCertificatePem(certPem);
+ }
+
+ @Override
+ public String getCertificatePem() {
+ if (updated != null) return updated.getCertificatePem();
+ return cached.getCertificatePem();
+ }
+
+ @Override
+ public void setCertificatePem(String certificate) {
+ getDelegateForUpdate();
+ updated.setCertificatePem(certificate);
+
+ }
+
+ @Override
public PrivateKey getPrivateKey() {
if (privateKey != null) return privateKey;
privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
@@ -345,6 +374,8 @@ public class RealmAdapter implements RealmModel {
setPrivateKeyPem(privateKeyPem);
}
+
+
@Override
public List<RequiredCredentialModel> getRequiredCredentials() {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index 8429aac..bcb5ad9 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -80,6 +80,8 @@ public class RealmEntity {
protected String publicKeyPem;
@Column(name="PRIVATE_KEY", length = 2048)
protected String privateKeyPem;
+ @Column(name="CERTIFICATE", length = 2048)
+ protected String certificatePem;
@Column(name="LOGIN_THEME")
protected String loginTheme;
@@ -432,5 +434,13 @@ public class RealmEntity {
public void setAttributes(Collection<RealmAttributeEntity> attributes) {
this.attributes = attributes;
}
+
+ public String getCertificatePem() {
+ return certificatePem;
+ }
+
+ public void setCertificatePem(String certificatePem) {
+ this.certificatePem = certificatePem;
+ }
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index c382ae8..5578b88 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -23,6 +23,7 @@ import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -44,6 +45,7 @@ public class RealmAdapter implements RealmModel {
protected EntityManager em;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
+ protected volatile transient X509Certificate certificate;
protected KeycloakSession session;
private PasswordPolicy passwordPolicy;
@@ -368,6 +370,32 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public X509Certificate getCertificate() {
+ if (certificate != null) return certificate;
+ certificate = KeycloakModelUtils.getCertificate(getCertificatePem());
+ return certificate;
+ }
+
+ @Override
+ public void setCertificate(X509Certificate certificate) {
+ this.certificate = certificate;
+ String certificatePem = KeycloakModelUtils.getPemFromCertificate(certificate);
+ setCertificatePem(certificatePem);
+
+ }
+
+ @Override
+ public String getCertificatePem() {
+ return realm.getCertificatePem();
+ }
+
+ @Override
+ public void setCertificatePem(String certificate) {
+ realm.setCertificatePem(certificate);
+
+ }
+
+ @Override
public String getPrivateKeyPem() {
return realm.getPrivateKeyPem();
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 9b9d436..ba51c13 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -25,6 +25,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -49,6 +50,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
+ protected volatile transient X509Certificate certificate;
private volatile transient PasswordPolicy passwordPolicy;
private volatile transient KeycloakSession session;
@@ -351,6 +353,33 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
@Override
+ public X509Certificate getCertificate() {
+ if (certificate != null) return certificate;
+ certificate = KeycloakModelUtils.getCertificate(getCertificatePem());
+ return certificate;
+ }
+
+ @Override
+ public void setCertificate(X509Certificate certificate) {
+ this.certificate = certificate;
+ String certificatePem = KeycloakModelUtils.getPemFromCertificate(certificate);
+ setCertificatePem(certificatePem);
+
+ }
+
+ @Override
+ public String getCertificatePem() {
+ return realm.getCertificatePem();
+ }
+
+ @Override
+ public void setCertificatePem(String certificate) {
+ realm.setCertificatePem(certificate);
+
+ }
+
+
+ @Override
public String getPrivateKeyPem() {
return realm.getPrivateKeyPem();
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index e804161..420ec6d 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -94,7 +94,6 @@ public class ApplicationResource {
public Response update(final ApplicationRepresentation rep) {
auth.requireManage();
- ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
try {
RepresentationToModel.updateApplication(rep, application);
return Response.noContent().build();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java
new file mode 100755
index 0000000..fe68c5b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java
@@ -0,0 +1,73 @@
+package org.keycloak.services.resources.admin;
+
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.KeyPurposeId;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.x509.X509V1CertificateGenerator;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
+import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+
+import javax.security.auth.x500.X500Principal;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.StreamingOutput;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.SignatureException;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClientCertificateResource {
+ protected RealmModel realm;
+ private RealmAuth auth;
+ protected ClientModel client;
+ protected KeycloakSession session;
+
+ public ClientCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session) {
+ this.realm = realm;
+ this.auth = auth;
+ this.client = client;
+ this.session = session;
+ }
+
+ @POST
+ public void generate() {
+ auth.requireManage();
+
+
+
+ }
+
+ @GET
+ @Path("/download/jks")
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ public StreamingOutput getJavaKeyStore(@QueryParam("realmCertificate") @DefaultValue("true") boolean realmCertificate) {
+ auth.requireView();
+ return null;
+
+ }
+
+
+}