]> WPIA git - gigi.git/commitdiff
ADD: root structure awareness.
authorFelix Dörre <felix@dogcraft.de>
Sat, 9 May 2015 20:16:40 +0000 (22:16 +0200)
committerFelix Dörre <felix@dogcraft.de>
Thu, 14 May 2015 23:16:32 +0000 (01:16 +0200)
src/org/cacert/gigi/database/DatabaseConnection.java
src/org/cacert/gigi/database/tableStructure.sql
src/org/cacert/gigi/database/upgrade/from_2.sql [new file with mode: 0644]
src/org/cacert/gigi/dbObjects/CACertificate.java [new file with mode: 0644]
src/org/cacert/gigi/dbObjects/Certificate.java
src/org/cacert/gigi/pages/account/certs/CertificateDisplay.templ
src/org/cacert/gigi/pages/account/certs/Certificates.java

index 009aa6a6ea750fc1b96b1d95f60df4e6d642a3fe..0d7e22b4f0e8461431d3819f533f264ade0fcdba 100644 (file)
@@ -15,7 +15,7 @@ import org.cacert.gigi.database.SQLFileManager.ImportType;
 
 public class DatabaseConnection {
 
-    public static final int CURRENT_SCHEMA_VERSION = 2;
+    public static final int CURRENT_SCHEMA_VERSION = 3;
 
     public static final int CONNECTION_TIMEOUT = 24 * 60 * 60;
 
index 85f9d779218c7b8582072e259f257ebcff21c3a7..e41bce894f377fe53208e498b3594eb03b21dd4d 100644 (file)
@@ -226,7 +226,7 @@ DROP TABLE IF EXISTS `cacerts`;
 CREATE TABLE `cacerts` (
   `id` int(3) NOT NULL AUTO_INCREMENT,
   `keyname` varchar(60) NOT NULL,
-  `subroot` int(2) NOT NULL,
+  `parentRoot` int(3) NOT NULL,
   `validFrom` datetime NULL DEFAULT NULL,
   `validTo` datetime NULL DEFAULT NULL,
   PRIMARY KEY (`id`),
@@ -347,4 +347,4 @@ CREATE TABLE `schemeVersion` (
   `version` int(5) NOT NULL,
   PRIMARY KEY (`version`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-INSERT INTO schemeVersion(version)  VALUES(2);
+INSERT INTO schemeVersion(version)  VALUES(3);
diff --git a/src/org/cacert/gigi/database/upgrade/from_2.sql b/src/org/cacert/gigi/database/upgrade/from_2.sql
new file mode 100644 (file)
index 0000000..c36fbd4
--- /dev/null
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS `cacerts`;
+CREATE TABLE `cacerts` (
+  `id` int(3) NOT NULL AUTO_INCREMENT,
+  `keyname` varchar(60) NOT NULL,
+  `link` varchar(160) NOT NULL,
+  `parentRoot` int(3) NOT NULL,
+  `validFrom` datetime NULL DEFAULT NULL,
+  `validTo` datetime NULL DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE (`keyname`)
+) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
diff --git a/src/org/cacert/gigi/dbObjects/CACertificate.java b/src/org/cacert/gigi/dbObjects/CACertificate.java
new file mode 100644 (file)
index 0000000..aa12d08
--- /dev/null
@@ -0,0 +1,175 @@
+package org.cacert.gigi.dbObjects;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.security.GeneralSecurityException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.cacert.gigi.database.DatabaseConnection;
+import org.cacert.gigi.database.GigiPreparedStatement;
+import org.cacert.gigi.database.GigiResultSet;
+
+public class CACertificate implements IdCachable {
+
+    final String keyname;
+
+    final int id;
+
+    CACertificate parent = null;
+
+    final X509Certificate cert;
+
+    final String link;
+
+    private CACertificate(int id) {
+        this.id = id;
+        GigiPreparedStatement conn = DatabaseConnection.getInstance().prepare("SELECT keyname, parentRoot, link FROM cacerts WHERE id = ?");
+        conn.setInt(1, id);
+        GigiResultSet res = conn.executeQuery();
+        if ( !res.next()) {
+            throw new IllegalArgumentException();
+        }
+        keyname = res.getString("keyname");
+        link = res.getString("link");
+        int parentRoot = res.getInt("parentRoot");
+        if (res.next()) {
+            throw new RuntimeException("DB is broken");
+        }
+        if (parentRoot == id) {
+            parent = this;
+        } else {
+            parent = getById(parentRoot);
+        }
+        try {
+            FileInputStream fis = new FileInputStream("config/ca/" + keyname + ".crt");
+            CertificateFactory cf = CertificateFactory.getInstance("X509");
+            cert = (X509Certificate) cf.generateCertificate(fis);
+        } catch (FileNotFoundException e) {
+            throw new Error(e);
+        } catch (GeneralSecurityException e) {
+            throw new Error(e);
+        }
+    }
+
+    public CACertificate getParent() {
+        return parent;
+    }
+
+    public X509Certificate getCertificate() {
+        return cert;
+    }
+
+    @Override
+    public String toString() {
+        return "CACertificate: " + keyname;
+    }
+
+    static {
+        try {
+            update();
+        } catch (CertificateException e) {
+            throw new Error(e);
+        } catch (FileNotFoundException e) {
+            throw new Error(e);
+        }
+    }
+
+    private static void update() throws CertificateException, FileNotFoundException {
+        File scandir = new File("config/ca");
+        CertificateFactory xf = CertificateFactory.getInstance("X509");
+        HashMap<X500Principal, X509Certificate> map = new HashMap<>();
+        HashMap<X500Principal, String> names = new HashMap<>();
+        for (File f : scandir.listFiles()) {
+            X509Certificate cert = (X509Certificate) xf.generateCertificate(new FileInputStream(f));
+            X500Principal princip = cert.getSubjectX500Principal();
+            map.put(princip, cert);
+            String name = f.getName();
+            names.put(princip, name.substring(0, name.length() - 4));
+        }
+        HashMap<X500Principal, Integer> inserted = new HashMap<>();
+        for (X509Certificate i : map.values()) {
+            if (inserted.containsKey(i.getSubjectX500Principal())) {
+                continue;
+            }
+            Deque<X509Certificate> toInserts = new ArrayDeque<>();
+            toInserts.add(i);
+            while ( !inserted.containsKey(i.getIssuerX500Principal()) && !i.getIssuerX500Principal().equals(i.getSubjectX500Principal())) {
+                i = map.get(i.getIssuerX500Principal());
+                toInserts.addFirst(i);
+            }
+            for (X509Certificate toInsert : toInserts) {
+
+                X500Principal subj = toInsert.getSubjectX500Principal();
+                boolean self = toInsert.getIssuerX500Principal().equals(subj);
+                GigiPreparedStatement q = DatabaseConnection.getInstance().prepare("SELECT id, parentRoot FROM cacerts WHERE keyname=?");
+                q.setString(1, names.get(subj));
+                GigiResultSet res = q.executeQuery();
+                int id;
+                if (res.next()) {
+                    id = res.getInt("id");
+                    if (res.getInt("parentRoot") != (self ? id : inserted.get(toInsert.getIssuerX500Principal()))) {
+                        throw new Error("Invalid DB structure: " + subj + "->" + inserted.get(toInsert.getIssuerX500Principal()) + " vs " + res.getInt("parentRoot"));
+                    }
+                } else {
+                    String link;
+                    String keyname = names.get(subj);
+                    if ( !keyname.contains("_")) {
+                        link = "http://g2.crt.cacert.org/g2/" + keyname + ".crt";
+                    } else {
+                        String[] parts = keyname.split("_");
+                        link = "http://g2.crt.cacert.org/g2/" + parts[1] + "/" + parts[0] + "-" + parts[2] + ".crt";
+
+                    }
+                    GigiPreparedStatement q2 = DatabaseConnection.getInstance().prepare("INSERT INTO cacerts SET parentRoot=?, keyname=?, link=?");
+                    q2.setInt(1, self ? 0 : inserted.get(toInsert.getIssuerX500Principal()));
+                    q2.setString(2, keyname);
+                    q2.setString(3, link);
+                    q2.execute();
+                    id = q2.lastInsertId();
+                    if (self) {
+                        GigiPreparedStatement q3 = DatabaseConnection.getInstance().prepare("UPDATE cacerts SET parentRoot=?, id=?");
+                        q3.setInt(1, id);
+                        q3.setInt(2, id);
+                        q3.execute();
+                    }
+                }
+                inserted.put(subj, id);
+            }
+        }
+    }
+
+    @Override
+    public int getId() {
+        return id;
+    }
+
+    private static ObjectCache<CACertificate> myCache = new ObjectCache<>();
+
+    public String getKeyname() {
+        return keyname;
+    }
+
+    public String getLink() {
+        return link;
+    }
+
+    public static synchronized CACertificate getById(int id) throws IllegalArgumentException {
+        CACertificate em = myCache.get(id);
+        if (em == null) {
+            myCache.put(em = new CACertificate(id));
+        }
+        return em;
+    }
+
+    public boolean isSelfsigned() {
+        return this == getParent();
+    }
+}
index 6882b8113018c4c41af40bf54f5844f176a9878b..479a183ae82a5c59d80c9ca4ae1649627572ed82 100644 (file)
@@ -132,6 +132,8 @@ public class Certificate {
 
     private String dnString;
 
+    private CACertificate ca;
+
     public Certificate(User owner, HashMap<String, String> dn, String md, String csr, CSRType csrType, CertificateProfile profile, SubjectAlternateName... sans) throws GigiApiException {
         if ( !owner.canIssue(profile)) {
             throw new GigiApiException("You are not allowed to issue these certificates.");
@@ -210,7 +212,7 @@ public class Certificate {
         if (id == 0) {
             return CertificateStatus.DRAFT;
         }
-        GigiPreparedStatement searcher = DatabaseConnection.getInstance().prepare("SELECT crt_name, created, revoked, serial FROM certs WHERE id=?");
+        GigiPreparedStatement searcher = DatabaseConnection.getInstance().prepare("SELECT crt_name, created, revoked, serial, caid FROM certs WHERE id=?");
         searcher.setInt(1, id);
         GigiResultSet rs = searcher.executeQuery();
         if ( !rs.next()) {
@@ -219,6 +221,7 @@ public class Certificate {
 
         crtName = rs.getString(1);
         serial = rs.getString(4);
+        ca = CACertificate.getById(rs.getInt("caid"));
         if (rs.getTimestamp(2) == null) {
             return CertificateStatus.DRAFT;
         }
@@ -293,6 +296,14 @@ public class Certificate {
 
     }
 
+    public CACertificate getParent() {
+        CertificateStatus status = getStatus();
+        if (status != CertificateStatus.REVOKED && status != CertificateStatus.ISSUED) {
+            throw new IllegalStateException(status + " is not wanted here.");
+        }
+        return ca;
+    }
+
     public X509Certificate cert() throws IOException, GeneralSecurityException {
         CertificateStatus status = getStatus();
         if (status != CertificateStatus.REVOKED && status != CertificateStatus.ISSUED) {
index ace4be515220374bc4f9de4a62866f6d9ba4dfa0..99ae5d3122fe3f6907a79d216ac01ecaa9e3dc3e 100644 (file)
@@ -1,4 +1,9 @@
-<a href='<?=$serial?>.crt'><?=_PEM encoded Certificate?></a><br/>
+<a href='<?=$serial?>.crt'><?=_PEM encoded Certificate?></a>
+<? foreach($trustchain) { ?>
+ <?=_issued by?> <a href='<?=$link?>'><?=$name?></a>
+<? } ?><br/>
+<a href='<?=$serial?>.crt?chain'><?=_PEM encoded Certificate Cain?></a><br/>
+<a href='<?=$serial?>.crt?chain&noAnchor'><?=_PEM encoded Certificate Cain (Excluding Anchor)?></a><br/>
 <a href='<?=$serial?>.cer'><?=_DER encoded Certificate?></a><br/>
 <a href='<?=$serial?>.cer?install'><?=_Install into browser.?></a><br/>
 <pre>
index e41a1b297914316ec12a591d8a8f4de52958fc56..5ae5ca63cdb8ea694f87784c658a0503f327aa33 100644 (file)
@@ -6,14 +6,18 @@ import java.net.URLEncoder;
 import java.security.GeneralSecurityException;
 import java.security.cert.X509Certificate;
 import java.util.HashMap;
+import java.util.Map;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.cacert.gigi.dbObjects.CACertificate;
 import org.cacert.gigi.dbObjects.Certificate;
 import org.cacert.gigi.dbObjects.User;
+import org.cacert.gigi.localisation.Language;
 import org.cacert.gigi.output.CertificateIterable;
+import org.cacert.gigi.output.template.IterableDataset;
 import org.cacert.gigi.output.template.Template;
 import org.cacert.gigi.pages.LoginPage;
 import org.cacert.gigi.pages.Page;
@@ -25,6 +29,31 @@ public class Certificates extends Page {
 
     public static final String PATH = "/account/certs";
 
+    static class TrustchainIterable implements IterableDataset {
+
+        CACertificate cert;
+
+        public TrustchainIterable(CACertificate cert) {
+            this.cert = cert;
+        }
+
+        @Override
+        public boolean next(Language l, Map<String, Object> vars) {
+            if (cert == null) {
+                return false;
+            }
+            vars.put("name", cert.getKeyname());
+            vars.put("link", cert.getLink());
+            if (cert.isSelfsigned()) {
+                cert = null;
+                return true;
+            }
+            cert = cert.getParent();
+            return true;
+        }
+
+    }
+
     public Certificates() {
         super("Certificates");
     }
@@ -67,6 +96,16 @@ public class Certificates extends Page {
             ServletOutputStream out = resp.getOutputStream();
             if (crt) {
                 out.println(PEM.encode("CERTIFICATE", cert.getEncoded()));
+                if (req.getParameter("chain") != null) {
+                    CACertificate ca = c.getParent();
+                    while ( !ca.isSelfsigned()) {
+                        out.println(PEM.encode("CERTIFICATE", ca.getCertificate().getEncoded()));
+                        ca = ca.getParent();
+                    }
+                    if (req.getParameter("noAnchor") == null) {
+                        out.println(PEM.encode("CERTIFICATE", ca.getCertificate().getEncoded()));
+                    }
+                }
             } else if (cer) {
                 out.write(cert.getEncoded());
             }
@@ -98,6 +137,7 @@ public class Certificates extends Page {
             }
             HashMap<String, Object> vars = new HashMap<>();
             vars.put("serial", URLEncoder.encode(serial, "UTF-8"));
+            vars.put("trustchain", new TrustchainIterable(c.getParent()));
             try {
                 vars.put("cert", c.cert());
             } catch (GeneralSecurityException e) {