From: Felix Dörre Date: Sat, 9 May 2015 20:16:40 +0000 (+0200) Subject: ADD: root structure awareness. X-Git-Url: https://code.wpia.club/?p=gigi.git;a=commitdiff_plain;h=3256b7b19512a2e161e4ae3a8db706d671dc066f ADD: root structure awareness. --- diff --git a/src/org/cacert/gigi/database/DatabaseConnection.java b/src/org/cacert/gigi/database/DatabaseConnection.java index 009aa6a6..0d7e22b4 100644 --- a/src/org/cacert/gigi/database/DatabaseConnection.java +++ b/src/org/cacert/gigi/database/DatabaseConnection.java @@ -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; diff --git a/src/org/cacert/gigi/database/tableStructure.sql b/src/org/cacert/gigi/database/tableStructure.sql index 85f9d779..e41bce89 100644 --- a/src/org/cacert/gigi/database/tableStructure.sql +++ b/src/org/cacert/gigi/database/tableStructure.sql @@ -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 index 00000000..c36fbd49 --- /dev/null +++ b/src/org/cacert/gigi/database/upgrade/from_2.sql @@ -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 index 00000000..aa12d08f --- /dev/null +++ b/src/org/cacert/gigi/dbObjects/CACertificate.java @@ -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 map = new HashMap<>(); + HashMap 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 inserted = new HashMap<>(); + for (X509Certificate i : map.values()) { + if (inserted.containsKey(i.getSubjectX500Principal())) { + continue; + } + Deque 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 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(); + } +} diff --git a/src/org/cacert/gigi/dbObjects/Certificate.java b/src/org/cacert/gigi/dbObjects/Certificate.java index 6882b811..479a183a 100644 --- a/src/org/cacert/gigi/dbObjects/Certificate.java +++ b/src/org/cacert/gigi/dbObjects/Certificate.java @@ -132,6 +132,8 @@ public class Certificate { private String dnString; + private CACertificate ca; + public Certificate(User owner, HashMap 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) { diff --git a/src/org/cacert/gigi/pages/account/certs/CertificateDisplay.templ b/src/org/cacert/gigi/pages/account/certs/CertificateDisplay.templ index ace4be51..99ae5d31 100644 --- a/src/org/cacert/gigi/pages/account/certs/CertificateDisplay.templ +++ b/src/org/cacert/gigi/pages/account/certs/CertificateDisplay.templ @@ -1,4 +1,9 @@ -
+ + + +
+
+


diff --git a/src/org/cacert/gigi/pages/account/certs/Certificates.java b/src/org/cacert/gigi/pages/account/certs/Certificates.java
index e41a1b29..5ae5ca63 100644
--- a/src/org/cacert/gigi/pages/account/certs/Certificates.java
+++ b/src/org/cacert/gigi/pages/account/certs/Certificates.java
@@ -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 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 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) {