keycloak-aplcache

generate/store realm certificate

10/11/2014 11:49:04 AM

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;
+
+    }
+
+
+}